diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 000000000..422802d21 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1,507 @@ +# NumSharp Project Instructions + +NumSharp is a .NET port of Python's NumPy library targeting **1-to-1 API and behavioral compatibility with NumPy 2.x (latest)**. + +## NumPy Reference Source + +A full clone of the NumPy repository is available at `src/numpy/`, checked out to **v2.4.2** (latest stable release). Use this as the authoritative reference for API behavior, edge cases, and implementation details when implementing or verifying NumSharp functions. + +## Core Principles + +1. **Match NumPy Exactly**: Run actual Python/NumPy code first, observe behavior, replicate in C# +2. **Edge Cases Matter**: NaN handling, empty arrays, type promotion, broadcasting, negative axis +3. **Breaking Changes OK**: Library was dormant; API stability is not a constraint +4. **Test From NumPy Output**: Tests should be based on running actual NumPy code + +## Supported Types (12) + +| NPTypeCode | C# Type | NPTypeCode | C# Type | +|------------|---------|------------|---------| +| Boolean | bool | Int64 | long | +| Byte | byte | UInt64 | ulong | +| Int16 | short | Char | char | +| UInt16 | ushort | Single | float | +| Int32 | int | Double | double | +| UInt32 | uint | Decimal | decimal | + +All operations must handle all 12 types via type switch pattern. + +## Architecture + +``` +NDArray Main class (like numpy.ndarray) +├── Storage UnmanagedStorage (raw pointers, not managed arrays) +├── Shape Dimensions, strides, offset calculation +└── TensorEngine Computation backend (DefaultEngine = pure C#) + +np Static API class (like `import numpy as np`) +├── np.random NumPyRandom (1-to-1 seed/state with NumPy) +└── np.* Functions in Creation/, Math/, Statistics/, Logic/, etc. +``` + +## Key Design Decisions + +| Decision | Rationale | +|----------|-----------| +| Unmanaged memory | Benchmarked fastest ~5y ago; Span/Memory immature then | +| Regen templating | ~200K lines generated for type-specific code | +| TensorEngine abstract | Future GPU/SIMD backends possible | +| View semantics | Slicing returns views (shared memory), not copies | + +## Critical: View Semantics + +**Slicing returns views, not copies!** Memory is shared: +```csharp +var view = original["2:5"]; // Shares memory with original +view[0] = 999; // Modifies original[2]! +var copy = original["2:5"].copy(); // Explicit copy +``` + +## Slicing Syntax + +```csharp +nd[":"] // All elements +nd["1:5"] // Elements 1-4 (stop exclusive) +nd["::2"] // Every 2nd element +nd["-1"] // Last element (reduces dimension) +nd["::-1"] // Reversed +nd[":, 0"] // All rows, first column +nd["..., -1"] // Ellipsis fills dimensions +``` + +--- + +## Capabilities Reference + +### Array Creation (`Creation/`) +| Function | File | +|----------|------| +| `np.array` | `np.array.cs` | +| `np.zeros`, `np.zeros_like` | `np.zeros.cs`, `np.zeros_like.cs` | +| `np.ones`, `np.ones_like` | `np.ones.cs`, `np.ones_like.cs` | +| `np.empty`, `np.empty_like` | `np.empty.cs`, `np.empty_like.cs` | +| `np.full`, `np.full_like` | `np.full.cs`, `np.full_like.cs` | +| `np.arange` | `np.arange.cs` | +| `np.linspace` | `np.linspace.cs` | +| `np.eye` | `np.eye.cs` | +| `np.meshgrid`, `np.mgrid` | `np.meshgrid.cs`, `np.mgrid.cs` | +| `np.copy` | `np.copy.cs` | +| `np.asarray`, `np.asanyarray` | `np.asarray.cs`, `np.asanyarray.cs` | +| `np.frombuffer` | `np.frombuffer.cs` | + +### Stacking & Joining (`Creation/`) +| Function | File | +|----------|------| +| `np.concatenate` | `np.concatenate.cs` | +| `np.stack` | `np.stack.cs` | +| `np.hstack` | `np.hstack.cs` | +| `np.vstack` | `np.vstack.cs` | +| `np.dstack` | `np.dstack.cs` | + +### Broadcasting (`Creation/`) +| Function | File | +|----------|------| +| `np.broadcast` | `np.broadcast.cs` | +| `np.broadcast_to` | `np.broadcast_to.cs` | +| `np.broadcast_arrays` | `np.broadcast_arrays.cs` | +| `np.are_broadcastable` | `np.are_broadcastable.cs` | + +### Math Functions (`Math/`) +| Function | File | +|----------|------| +| `np.sum` | `np.sum.cs` | +| `np.prod` | `NDArray.prod.cs` | +| `np.cumsum` | `NDArray.cumsum.cs` | +| `np.power` | `np.power.cs` | +| `np.sqrt` | `np.sqrt.cs` | +| `np.abs`, `np.absolute` | `np.absolute.cs` | +| `np.sign` | `np.sign.cs` | +| `np.floor`, `np.ceil` | `np.floor.cs`, `np.ceil.cs` | +| `np.round` | `np.round.cs` | +| `np.clip` | `np.clip.cs` | +| `np.modf` | `np.modf.cs` | +| `np.maximum`, `np.minimum` | `np.maximum.cs`, `np.minimum.cs` | +| `np.log`, `np.log2`, `np.log10`, `np.log1p` | `np.log.cs` | +| `np.exp`, `np.exp2`, `np.expm1` | `Statistics/np.exp.cs` | +| `np.sin`, `np.cos`, `np.tan` | `np.sin.cs`, `np.cos.cs`, `np.tan.cs` | + +### Statistics (`Statistics/`) +| Function | File | +|----------|------| +| `np.mean`, `nd.mean()` | `np.mean.cs`, `NDArray.mean.cs` | +| `np.std`, `nd.std()` | `np.std.cs`, `NDArray.std.cs` | +| `np.var`, `nd.var()` | `np.var.cs`, `NDArray.var.cs` | +| `np.amax`, `nd.amax()` | `Sorting/np.amax.cs`, `NDArray.amax.cs` | +| `np.amin`, `nd.amin()` | `Sorting/np.min.cs`, `NDArray.amin.cs` | +| `np.argmax`, `nd.argmax()` | `Sorting/np.argmax.cs`, `NDArray.argmax.cs` | +| `np.argmin`, `nd.argmin()` | `Sorting_Searching_Counting/np.argmax.cs`, `NDArray.argmin.cs` | + +### Sorting & Searching (`Sorting_Searching_Counting/`) +| Function | File | +|----------|------| +| `np.argsort`, `nd.argsort()` | `np.argsort.cs`, `ndarray.argsort.cs` | +| `np.searchsorted` | `np.searchsorted.cs` | + +### Linear Algebra (`LinearAlgebra/`) +| Function | File | +|----------|------| +| `np.dot`, `nd.dot()` | `np.dot.cs`, `NDArray.dot.cs` | +| `np.matmul` | `np.matmul.cs` | +| `np.outer` | `np.outer.cs` | +| ~~`np.linalg.norm`~~ | `np.linalg.norm.cs` | **DEAD CODE**: declared `private static` — not accessible | +| `nd.matrix_power()` | `NDArray.matrix_power.cs` | | +| ~~`nd.inv()`~~ | `NdArray.Inv.cs` | **DEAD CODE**: returns null | +| ~~`nd.qr()`~~ | `NdArray.QR.cs` | **DEAD CODE**: returns default | +| ~~`nd.svd()`~~ | `NdArray.SVD.cs` | **DEAD CODE**: returns default | +| ~~`nd.lstsq()`~~ | `NdArray.LstSq.cs` | **DEAD CODE**: named `lstqr`, returns null | +| ~~`nd.multi_dot()`~~ | `NdArray.multi_dot.cs` | **DEAD CODE**: returns null | + +### Shape Manipulation (`Manipulation/`) +| Function | File | +|----------|------| +| `np.reshape`, `nd.reshape()` | `np.reshape.cs` | +| `np.transpose`, `nd.T` | `np.transpose.cs`, `NdArray.Transpose.cs` | +| `np.ravel`, `nd.ravel()` | `np.ravel.cs`, `NDArray.ravel.cs` | +| `nd.flatten()` | `NDArray.flatten.cs` | +| `np.squeeze` | `np.squeeze.cs` | +| `np.expand_dims` | `np.expand_dims.cs` | +| `np.swapaxes` | `np.swapaxes.cs`, `NdArray.swapaxes.cs` | +| `np.moveaxis` | `np.moveaxis.cs` | +| `np.rollaxis` | `np.rollaxis.cs` | +| `nd.roll()` | `NDArray.roll.cs` | Partial: only Int32/Single/Double with axis; no-axis returns null | +| `np.atleast_1d/2d/3d` | `np.atleastd.cs` | +| `np.unique`, `nd.unique()` | `np.unique.cs`, `NDArray.unique.cs` | +| `np.repeat` | `np.repeat.cs` | +| ~~`nd.delete()`~~ | `NdArray.delete.cs` | **DEAD CODE**: returns null | +| `np.copyto` | `np.copyto.cs` | + +### Logic Functions (`Logic/`) +| Function | File | +|----------|------| +| `np.all` | `np.all.cs` | All dtypes; with-axis works | +| `np.any` | `np.any.cs` | All dtypes; with-axis **BUGGY** (always throws) | +| ~~`np.allclose`~~ | `np.allclose.cs` | **DEAD CODE**: depends on `np.isclose` which returns null | +| `np.array_equal` | `np.array_equal.cs` | | +| `np.isscalar` | `np.is.cs` | | +| ~~`np.isnan`~~ | `np.is.cs` | **DEAD CODE**: `DefaultEngine.IsNan` returns null | +| ~~`np.isfinite`~~ | `np.is.cs` | **DEAD CODE**: `DefaultEngine.IsFinite` returns null | +| ~~`np.isclose`~~ | `np.is.cs` | **DEAD CODE**: `DefaultEngine.IsClose` returns null | +| `np.find_common_type` | `np.find_common_type.cs` | | + +### Comparison Operators (`Operations/Elementwise/`) +| Operator | File | +|----------|------| +| `==` (element-wise) | `NDArray.Equals.cs` | +| `!=` | `NDArray.NotEquals.cs` | +| `>`, `>=` | `NDArray.Greater.cs` | +| `<`, `<=` | `NDArray.Lower.cs` | +| ~~`&` (AND)~~ | `NDArray.AND.cs` | **DEAD CODE**: returns null | +| ~~`\|` (OR)~~ | `NDArray.OR.cs` | **DEAD CODE**: returns null | +| `!` (NOT) | `NDArray.NOT.cs` | + +### Arithmetic Operators (`Operations/Elementwise/`) +| Operator | File | +|----------|------| +| `+`, `-`, `*`, `/`, `%`, unary `-` | `NDArray.Primitive.cs` | + +### Indexing & Selection (`Selection/`) +| Feature | File | +|---------|------| +| Integer/slice indexing | `NDArray.Indexing.cs` | +| Boolean masking | `NDArray.Indexing.Masking.cs` | Read works; setter throws NotImplementedException | +| Fancy indexing (NDArray indices) | `NDArray.Indexing.Selection.cs` | +| `np.nonzero` | `Indexing/np.nonzero.cs` | + +### Random Sampling (`RandomSampling/`) +| Function | File | +|----------|------| +| `np.random.rand` | `np.random.rand.cs` | +| `np.random.randn` | `np.random.randn.cs` | +| `np.random.randint` | `np.random.randint.cs` | +| `np.random.uniform` | `np.random.uniform.cs` | +| `np.random.choice` | `np.random.choice.cs` | +| `np.random.shuffle` | `np.random.shuffle.cs` | +| `np.random.permutation` | `np.random.permutation.cs` | +| `np.random.beta` | `np.random.beta.cs` | +| `np.random.binomial` | `np.random.binomial.cs` | +| `np.random.gamma` | `np.random.gamma.cs` | +| `np.random.poisson` | `np.random.poisson.cs` | +| `np.random.exponential` | `np.random.exponential.cs` | +| `np.random.geometric` | `np.random.geometric.cs` | +| `np.random.lognormal` | `np.random.lognormal.cs` | +| `np.random.chisquare` | `np.random.chisquare.cs` | +| `np.random.bernoulli` | `np.random.bernoulli.cs` | + +### File I/O (`APIs/`) +| Function | File | +|----------|------| +| `np.save` (`.npy`) | `np.save.cs` | +| `np.load` (`.npy`, `.npz`) | `np.load.cs` | +| `np.fromfile` | `np.fromfile.cs` | +| `nd.tofile()` | `np.tofile.cs` | + +### Other APIs (`APIs/`) +| Function | File | +|----------|------| +| `np.size` | `np.size.cs` | +| `np.cumsum` | `np.cumsum.cs` | + +--- + +## Core Source Files + +| Component | Location | +|-----------|----------| +| NDArray | `Backends/NDArray.cs` | +| UnmanagedStorage | `Backends/Unmanaged/UnmanagedStorage.cs` | +| Shape | `View/Shape.cs` | +| Slice | `View/Slice.cs` | +| TensorEngine | `Backends/TensorEngine.cs` | +| DefaultEngine | `Backends/Default/DefaultEngine.*.cs` | +| np API | `APIs/np.cs` | +| Iterators | `Backends/Iterators/NDIterator.cs`, `MultiIterator.cs` | +| Type info | `Utilities/InfoOf.cs` | +| Generic NDArray | `Generics/NDArray\`1.cs` | + +--- + +## Implementation Patterns + +### Pattern 1: Compose existing np functions +```csharp +public static NDArray std(NDArray a, int? axis = null, ...) +{ + var mean_val = np.mean(a, axis, keepdims: true); + return np.sqrt(np.mean(np.power(a - mean_val, 2), axis)); +} +``` + +### Pattern 2: Delegate to TensorEngine +```csharp +public static NDArray sum(NDArray a, int? axis = null, ...) +{ + return a.TensorEngine.Sum(a, axis, typeCode, keepdims); +} +``` +Use Pattern 2 when low-level optimization is needed. + +## Type Switch Pattern + +```csharp +switch (nd.typecode) +{ + case NPTypeCode.Boolean: return Process(nd); + case NPTypeCode.Byte: return Process(nd); + case NPTypeCode.Int16: return Process(nd); + case NPTypeCode.UInt16: return Process(nd); + case NPTypeCode.Int32: return Process(nd); + case NPTypeCode.UInt32: return Process(nd); + case NPTypeCode.Int64: return Process(nd); + case NPTypeCode.UInt64: return Process(nd); + case NPTypeCode.Char: return Process(nd); + case NPTypeCode.Double: return Process(nd); + case NPTypeCode.Single: return Process(nd); + case NPTypeCode.Decimal: return Process(nd); + default: throw new NotSupportedException(); +} +``` + +## Build & Test + +```bash +dotnet build -v q --nologo "-clp:NoSummary;ErrorsOnly" -p:WarningLevel=0 +dotnet test -v q --nologo "-clp:ErrorsOnly" test/NumSharp.UnitTest/NumSharp.UnitTest.csproj +``` + +## Scripting with `dotnet run` (.NET 10 file-based apps) + +### Accessing Internal Members + +NumSharp has many key types/fields/methods marked `internal` (Shape.dimensions, Shape.strides, NDArray.Storage, np._FindCommonType, etc.). To access them from a `dotnet run` script, override the assembly name to match an existing `InternalsVisibleTo` entry: + +```csharp +#:project path/to/src/NumSharp.Core +#:property AssemblyName=NumSharp.DotNetRunScript +#:property PublishAot=false +``` + +**How it works:** NumSharp declares `[assembly: InternalsVisibleTo("NumSharp.DotNetRunScript")]` in `src/NumSharp.Core/Assembly/Properties.cs`. The `#:property AssemblyName=NumSharp.DotNetRunScript` directive overrides the script's assembly name (which normally derives from the filename) to match, granting full access to all `internal` and `protected internal` members. + +### Accessing Unsafe code +NumSharp uses unsafe in many places, hence include `#:property AllowUnsafeBlocks=true` in scripts. + +### Script Template (copy-paste ready) + +```csharp +#:project path/to/src/NumSharp.Core +#:property AssemblyName=NumSharp.DotNetRunScript +#:property PublishAot=false +#:property AllowUnsafeBlocks=true +``` + +### Quick One-Liners + +```bash +# Run a script with full internal access +dotnet run my_script.cs + +# Compare NumPy vs NumSharp type promotion +dotnet run script.cs # where script.cs contains: +# var ct = np._FindCommonType(np.array(1), np.array(1.5)); +# Console.WriteLine(ct); // Double + +# Inspect Shape internals +dotnet run script.cs # where script.cs contains: +# var s = new Shape(new int[]{2,3,4}); +# Console.WriteLine($"dims={string.Join(",",s.dimensions)} strides={string.Join(",",s.strides)} size={s.size}"); + +# Check ViewInfo after slicing +dotnet run script.cs # where script.cs contains: +# var arr = np.arange(24).reshape(2,3,4); +# var sliced = arr["1, :, ::2"]; +# Console.WriteLine($"ViewInfo: {sliced.Shape.ViewInfo != null}, BroadcastInfo: {sliced.Shape.BroadcastInfo != null}"); +``` + +### Key Internal Members Available + +| Member | What it exposes | +|--------|----------------| +| `shape.dimensions` | Raw int[] of dimension sizes | +| `shape.strides` | Raw int[] of stride values | +| `shape.size` | Total element count | +| `shape.ViewInfo` | Slice/view metadata (null if not a view) | +| `shape.BroadcastInfo` | Broadcast metadata (null if not broadcast) | +| `arr.Storage` | Underlying `UnmanagedStorage` | +| `arr.GetTypeCode` | `NPTypeCode` of the array | +| `arr.Array` | `IArraySlice` — raw data access | +| `np._FindCommonType(...)` | Type promotion logic | +| `np.powerOrder` | Type promotion ordering | +| `NPTypeCode.GetGroup()` | Type category (int/uint/float/etc.) | +| `NPTypeCode.GetPriority()` | Type priority for promotion | +| `NPTypeCode.AsNumpyDtypeName()` | NumPy dtype name (e.g. "int32") | +| `Shape.NewScalar()` | Create scalar shapes | +| `Shape.ComputeHashcode()` | Recalculate shape hash | + +## Adding New Features + +1. Read NumPy docs for the function +2. **Run actual Python code** to observe exact behavior and fuzzy all possible inputs to define a behavior matrix. +3. Check existing similar implementations +4. Implement behavior matching exactly that of numpy. +5. Write tests based on observed NumPy output +6. Handle all 12 dtypes + +--- + +## Q&A - Design & Architecture + +**Q: Why unmanaged memory instead of Span/Memory?** +A: Extensive benchmarking ~5 years ago showed unmanaged memory was fastest. Span/Memory weren't mature across the .NET ecosystem then. NDArray is self-managed memory allocation optimized for performance. + +**Q: Why Regen templating instead of T4 or source generators?** +A: Original needs felt too complicated for alternatives. Migration to T4 is possible but not prioritized. The ~200K lines of generated code is acceptable if it works correctly. + +**Q: Why is TensorEngine abstracted?** +A: To support potential future backends (GPU/CUDA, SIMD intrinsics, MKL/BLAS). Not implemented yet, but the architecture allows it. + +**Q: How closely does the API match NumPy?** +A: Goal is as close as possible - all edge cases included (NaN handling, multi-type operations, broadcasting). Target is NumPy 2.x (latest version), upgraded from original 1.x target. + +**Q: Does np.random match NumPy's random state/seed behavior?** +A: Yes, 1-to-1 matching. + +**Q: What are the primary use cases?** +A: Anything that can use the capabilities - porting Python ML code, standalone .NET scientific computing, integration with TensorFlow.NET/ML.NET. + +**Q: Are there areas of known fragility?** +A: Slicing/broadcasting system is complex with ViewInfo and BroadcastInfo interactions - fragile but working. + +**Q: How is NumPy compatibility validated?** +A: Written by hand based on NumPy docs and original tests. Testing philosophy: run actual NumPy code, observe output, replicate 1-to-1 in C#. + +**Q: What's the pattern for adding new np.* functions?** +A: Sometimes uses other np functions (no DefaultEngine needed). Sometimes requires DefaultEngine for optimization. Tests should be based on actually running NumPy code and imitating the outcome. + +**Q: Are breaking changes acceptable?** +A: Yes - breaking changes are accepted to align with NumPy 2.x behavior. + +**Q: What needs the most work?** +A: Implementations that differ from original NumPy 2.x behavior. A comprehensive API mapping expedition (NumSharp vs NumPy 2.x) is planned to identify: what exists, what's missing, what has behavioral differences. + +--- + +## Q&A - Core Components + +**Q: What are the three pillars of NumSharp?** +A: `NDArray` (user-facing API), `UnmanagedStorage` (raw memory management), and `Shape` (dimensions, strides, coordinate translation). They work together: NDArray wraps Storage which uses Shape for offset calculations. + +**Q: What is Shape responsible for?** +A: Dimensions, strides, coordinate-to-offset translation, contiguity tracking, and slice/broadcast info. Key properties: `IsScalar`, `IsContiguous`, `IsSliced`, `IsBroadcasted`. Methods: `GetOffset(coords)`, `GetCoordinates(offset)`. + +**Q: How does slicing work internally?** +A: The `Slice` class parses Python notation (e.g., "1:5:2") into `Start`, `Stop`, `Step`. It converts to `SliceDef` (absolute indices) for computation. `SliceDef.Merge()` handles recursive slicing (slice of a slice). + +**Q: What are the special Slice instances?** +A: `Slice.All` (`:` - all elements), `Slice.Ellipsis` (`...` - fill dimensions), `Slice.NewAxis` (insert dimension), `Slice.Index(n)` (single element, reduces dimensionality). + +**Q: What is NDIterator used for?** +A: Traversing arrays with different memory layouts. Handles contiguous (fast pointer increment) and sliced (uses GetOffset) arrays. Has `MoveNext()`, `HasNext()`, `Reset()`. AutoReset mode for broadcasting smaller arrays. + +**Q: What is MultiIterator?** +A: Handles paired iteration for broadcasting. `MultiIterator.Assign(lhs, rhs)` copies with broadcasting. `GetIterators(lhs, rhs, broadcast)` creates synchronized iterators. + +**Q: How does broadcasting work?** +A: Shapes align from the right. Dimensions must be equal OR one must be 1. Dimension of 1 "stretches" to match. Implemented via `DefaultEngine.Broadcast()` which resolves compatible shapes. + +**Q: What is InfoOf?** +A: Static type information cache to avoid runtime reflection. Provides `InfoOf.Size` (bytes), `InfoOf.NPTypeCode`, `InfoOf.Zero`, `InfoOf.MaxValue/MinValue`. + +**Q: What is NDArray?** +A: Generic typed wrapper providing type-safe access. Returns `T` from indexer instead of NDArray. Has typed `Address` pointer (`T*`) and `Array` property (`ArraySlice`). + +**Q: When does DefaultEngine use parallelization?** +A: For arrays exceeding 85,000 elements (`ParallelAbove = 84999`). Uses `Parallel.For` for large arrays, sequential loop for smaller ones. + +--- + +## Q&A - Operations & Operators + +**Q: How do arithmetic operators work?** +A: All operators (`+`, `-`, `*`, `/`, `%`, unary `-`) are defined in `NDArray.Primitive.cs`. They delegate to `TensorEngine.Add()`, `Subtract()`, etc. Scalar operands are wrapped via `NDArray.Scalar()`. + +**Q: How do comparison operators work?** +A: Element-wise comparisons (`==`, `!=`, `>`, `<`, etc.) return `NDArray`. Defined in `NDArray.Equals.cs`, `NDArray.Greater.cs`, etc. Support broadcasting. + +**Q: What indexing modes are supported?** +A: Integer indices, string slices (`"1:3, :"`), Slice objects, boolean masks, fancy indexing (NDArray indices), and mixed combinations. All in `Selection/NDArray.Indexing*.cs`. + +**Q: How is linear algebra implemented?** +A: Core ops (`dot`, `matmul`) in `LinearAlgebra/`. Advanced decompositions (`inv`, `qr`, `svd`, `lstsq`) are stub methods that return null/default — the LAPACK native bindings they depended on have been removed. + +--- + +## Q&A - Development + +**Q: What's in the test suite?** +A: MSTest framework in `test/NumSharp.UnitTest/`. Many tests adapted from NumPy's own test suite. Decent coverage but gaps in edge cases. + +**Q: What .NET version is targeted?** +A: Library and tests multi-target `net8.0` and `net10.0`. Dropped `netstandard2.0` in the dotnet810 branch upgrade. + +**Q: What are the main dependencies?** +A: No external runtime dependencies. `System.Memory` and `System.Runtime.CompilerServices.Unsafe` (previously NuGet packages) are built into the .NET 8+ runtime. + +**Q: What projects use NumSharp?** +A: TensorFlow.NET, ML.NET integrations, Gym.NET, Pandas.NET, and various scientific computing projects. + +**Q: Can I save/load NumPy files?** +A: Yes. `np.save()` writes `.npy` files, `np.load()` reads both `.npy` and `.npz` archives. Compatible with Python NumPy files. + +**Q: What random distributions are supported?** +A: Uniform, normal (randn), integers, beta, binomial, gamma, poisson, exponential, geometric, lognormal, chi-square, bernoulli. All in `RandomSampling/`. + +--- + +## Detailed Documentation + +@ARCHITECTURE.md for comprehensive technical details and @CONTRIBUTING.md for development workflow. diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..2c93a7fe7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/numpy"] + path = src/numpy + url = https://github.com/numpy/numpy.git diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000..1debe7ab8 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,690 @@ +# NumSharp Architecture Guide + +This document provides an in-depth technical overview of the NumSharp library internals, design decisions, and development practices. + +## Table of Contents + +1. [Project Overview](#project-overview) +2. [Core Architecture](#core-architecture) +3. [Memory Management](#memory-management) +4. [Type System](#type-system) +5. [API Layer](#api-layer) +6. [Slicing and Views](#slicing-and-views) +7. [Broadcasting](#broadcasting) +8. [Iterator System](#iterator-system) +9. [Code Generation](#code-generation) +10. [Development Workflow](#development-workflow) +11. [Technical Debt & Known Issues](#technical-debt--known-issues) +12. [Future Roadmap](#future-roadmap) + +--- + +## Project Overview + +NumSharp is a .NET port of Python's NumPy library, providing n-dimensional array operations for scientific computing in C#. The library aims to match NumPy's API as closely as possible, including edge cases like NaN handling, multi-type operations, and broadcasting semantics. + +### Goals + +- **API Compatibility**: Match NumPy 2.x API (upgraded from original 1.x target) +- **1-to-1 Behavior**: Replicate NumPy behavior exactly, including random state/seed +- **Performance**: Achieve competitive performance through unmanaged memory and unsafe code +- **Ecosystem Integration**: Support TensorFlow.NET, ML.NET, and other .NET ML frameworks + +### Project Structure + +``` +NumSharp/ +├── src/ +│ └── NumSharp.Core/ # Main library +│ ├── APIs/ # np.* static entry points +│ ├── Backends/ # TensorEngine, Storage, Iterators +│ │ ├── Default/ # Pure C# engine implementation +│ │ ├── Unmanaged/ # Memory management +│ │ ├── Iterators/ # NDIterator system +│ │ └── LAPACK/ # Linear algebra bindings +│ ├── Creation/ # np.zeros, np.arange, np.ones, etc. +│ ├── Math/ # np.sum, np.sin, np.log, etc. +│ ├── LinearAlgebra/ # np.dot, np.matmul, np.linalg.* +│ ├── Manipulation/ # reshape, transpose, flatten +│ ├── RandomSampling/ # np.random.* +│ ├── Statistics/ # mean, std, var, argmax +│ ├── Logic/ # np.all, np.any, np.allclose +│ ├── Selection/ # Indexing, slicing, masking +│ ├── Generics/ # NDArray typed wrapper +│ ├── View/ # Shape, Slice, ViewInfo +│ ├── Operations/ # Operator overloads +│ └── Utilities/ # Type helpers, converters +├── test/ +│ └── NumSharp.UnitTest/ # MSTest unit tests +├── examples/ +│ └── NeuralNetwork.NumSharp/ # Neural network example +└── docs/ # Documentation assets +``` + +--- + +## Core Architecture + +### Three Pillars: NDArray, UnmanagedStorage, Shape + +The library is built on three fundamental classes that work together: + +``` +┌─────────────────────────────────────────────────────────┐ +│ NDArray │ +│ - Public API surface │ +│ - Operator overloads (+, -, *, /, indexing) │ +│ - References TensorEngine for computations │ +├─────────────────────────────────────────────────────────┤ +│ UnmanagedStorage │ +│ - Holds raw data in unmanaged memory │ +│ - Manages ArraySlice for each dtype │ +│ - Handles allocation, slicing views, data access │ +├─────────────────────────────────────────────────────────┤ +│ Shape │ +│ - Dimensions and strides │ +│ - Coordinate ↔ offset translation │ +│ - Slicing, broadcasting, contiguity tracking │ +└─────────────────────────────────────────────────────────┘ +``` + +### NDArray + +`NDArray` is the primary user-facing class, analogous to `numpy.ndarray`: + +```csharp +// Key properties +public Shape Shape { get; } // Dimensions +public Type dtype { get; } // Element type +public int ndim { get; } // Number of dimensions +public int size { get; } // Total element count +public int[] strides { get; } // Byte strides per dimension +public UnmanagedStorage Storage { get; } +public TensorEngine TensorEngine { get; } + +// Key operations +public NDArray this[string slice] { get; set; } // "1:3, :, -1" +public NDArray reshape(params int[] shape); +public NDArray T { get; } // Transpose +public NDArray astype(Type dtype); +``` + +### TensorEngine + +`TensorEngine` is an abstract class defining all computational operations. This abstraction exists to potentially support alternative backends (GPU, SIMD, MKL) in the future. + +```csharp +public abstract class TensorEngine +{ + // Allocation + public abstract UnmanagedStorage GetStorage(NPTypeCode typeCode); + + // Arithmetic + public abstract NDArray Add(in NDArray lhs, in NDArray rhs); + public abstract NDArray Subtract(in NDArray lhs, in NDArray rhs); + public abstract NDArray Multiply(NDArray lhs, NDArray rhs); + public abstract NDArray Divide(in NDArray lhs, in NDArray rhs); + + // Reduction + public abstract NDArray ReduceAdd(in NDArray arr, int? axis_, bool keepdims, ...); + public abstract NDArray ReduceArgMax(NDArray arr, int? axis_); + + // Unary functions + public abstract NDArray Sqrt(in NDArray nd, NPTypeCode? typeCode); + public abstract NDArray Log(in NDArray nd, NPTypeCode? typeCode); + public abstract NDArray Exp(in NDArray nd, NPTypeCode? typeCode); + // ... 30+ more operations + + // Linear algebra + public abstract NDArray Dot(in NDArray x, in NDArray y); + public abstract NDArray Matmul(NDArray lhs, NDArray rhs); +} +``` + +The `DefaultEngine` is the current implementation - pure micro-optimized C# that uses `Parallel.For` for arrays exceeding 85,000 elements. + +--- + +## Memory Management + +### Why Unmanaged Memory? + +NumSharp uses unmanaged memory (raw pointers) rather than managed arrays or `Span`/`Memory`. This decision was made ~5 years ago based on extensive benchmarking when Span/Memory were not yet properly supported across the .NET ecosystem. + +**Benefits:** +- Zero-copy slicing (views share underlying memory) +- Direct pointer arithmetic for maximum performance +- No GC pressure for large arrays +- Interop-friendly for native libraries + +**Trade-offs:** +- Requires careful memory management +- Must use `unsafe` code blocks +- Manual disposal considerations + +### UnmanagedStorage + +```csharp +public class UnmanagedStorage +{ + internal IArraySlice InternalArray; // Type-erased ArraySlice + + public Shape Shape { get; } + public Type DType { get; } + public NPTypeCode TypeCode { get; } + public unsafe void* Address { get; } // Raw pointer to data + + // Get typed view + public ArraySlice GetData() where T : unmanaged; + + // Slicing returns views, not copies + public UnmanagedStorage GetView(params Slice[] slices); +} +``` + +### ArraySlice + +The generic `ArraySlice` wraps unmanaged memory with type safety: + +```csharp +public readonly struct ArraySlice : IArraySlice where T : unmanaged +{ + public readonly unsafe T* Address; + public readonly int Count; + + public ref T this[int index] { get; } + public Span AsSpan(); +} +``` + +--- + +## Type System + +### Supported Types (NPTypeCode) + +NumSharp supports 12 primitive types: + +| NPTypeCode | C# Type | Size (bytes) | +|------------|---------|--------------| +| Boolean | bool | 1 | +| Byte | byte | 1 | +| Int16 | short | 2 | +| UInt16 | ushort | 2 | +| Int32 | int | 4 | +| UInt32 | uint | 4 | +| Int64 | long | 8 | +| UInt64 | ulong | 8 | +| Char | char | 2 | +| Single | float | 4 | +| Double | double | 8 | +| Decimal | decimal | 16 | + +### InfoOf - Type Information Cache + +To avoid runtime reflection costs, type information is cached statically: + +```csharp +public class InfoOf +{ + public static readonly int Size; // Byte size + public static readonly NPTypeCode NPTypeCode; + public static readonly T Zero; // default(T) + public static readonly T MaxValue; + public static readonly T MinValue; +} + +// Usage +var size = InfoOf.Size; // 8 +var code = InfoOf.NPTypeCode; // NPTypeCode.Single +``` + +### NDArray - Generic Typed Wrapper + +For type-safe operations, `NDArray` provides a generic wrapper: + +```csharp +public class NDArray : NDArray where T : unmanaged +{ + public new T this[params int[] indices] { get; set; } + public new ArraySlice Array { get; } + public new unsafe T* Address { get; } + public new NDArray this[string slice] { get; } +} + +// Usage +NDArray arr = np.zeros(3, 4); +float val = arr[1, 2]; // Direct typed access +``` + +--- + +## API Layer + +### The `np` Static Class + +The `np` class is the primary entry point, mirroring Python's `import numpy as np`: + +```csharp +public static partial class np +{ + // Type aliases (matching NumPy) + public static readonly Type float64 = typeof(double); + public static readonly Type float32 = typeof(float); + public static readonly Type int32 = typeof(int); + public static readonly Type int64 = typeof(long); + public static readonly Type bool_ = typeof(bool); + + // Constants + public const double pi = Math.PI; + public const double e = Math.E; + public static readonly double nan = double.NaN; + public static readonly double inf = double.PositiveInfinity; + + // Random module + public static NumPyRandom random { get; } + + // Creation: np.zeros, np.ones, np.arange, np.linspace, etc. + // Math: np.sum, np.mean, np.sin, np.cos, np.exp, np.log, etc. + // Linear algebra: np.dot, np.matmul, np.linalg.* + // Manipulation: np.reshape, np.transpose, np.concatenate, etc. +} +``` + +### Function Implementation Patterns + +There are two patterns for implementing `np.*` functions: + +**Pattern 1: Delegating to TensorEngine** +```csharp +// np.sum delegates to engine +public static NDArray sum(NDArray a, int? axis = null, ...) +{ + return a.TensorEngine.Sum(a, axis, typeCode, keepdims); +} +``` + +**Pattern 2: Composing other np functions** +```csharp +// np.std composes np.mean and other operations +public static NDArray std(NDArray a, int? axis = null, ...) +{ + var mean = np.mean(a, axis, keepdims: true); + var diff = a - mean; + var sq = np.power(diff, 2); + return np.sqrt(np.mean(sq, axis, keepdims: keepdims)); +} +``` + +--- + +## Slicing and Views + +### Slice Class + +The `Slice` class parses and represents Python-style slice notation: + +```csharp +public class Slice +{ + public int? Start; // null = from beginning + public int? Stop; // null = to end + public int Step; // default 1 + public bool IsIndex; // Single element, reduces dimension + public bool IsEllipsis; // ... fills remaining dimensions + public bool IsNewAxis; // np.newaxis inserts dimension + + // Special instances + public static readonly Slice All; // ":" + public static readonly Slice None; // "0:0" + public static readonly Slice Ellipsis; // "..." + public static readonly Slice NewAxis; // "np.newaxis" + + // Parsing + public static Slice[] ParseSlices(string notation); // "1:3, :, -1" +} +``` + +### Slice Examples + +```csharp +nd[":"] // All elements +nd["1:5"] // Elements 1-4 +nd["::2"] // Every other element +nd["-1"] // Last element (reduces dimension) +nd["1::-1"] // Reverse from index 1 +nd[":, 0"] // All rows, first column +nd["..., -1"] // Last element of last dimension +``` + +### View Semantics + +**Critical**: Slicing returns views, not copies. The view shares memory with the original: + +```csharp +var original = np.arange(10); +var view = original["2:5"]; // View, shares memory +view[0] = 999; // Modifies original[2]! + +var copy = original["2:5"].copy(); // Explicit copy +``` + +### SliceDef - Internal Representation + +For efficient computation, slices are converted to `SliceDef`: + +```csharp +public struct SliceDef +{ + public int Start; // Absolute start index + public int Step; // Step size (can be negative) + public int Count; // Number of elements (-1 = single index) + + // Merge handles recursive slicing + public SliceDef Merge(SliceDef other); +} +``` + +--- + +## Broadcasting + +Broadcasting allows operations between arrays of different shapes by virtually expanding dimensions: + +```csharp +var a = np.ones(3, 4); // Shape: (3, 4) +var b = np.ones(4); // Shape: (4,) +var c = a + b; // Broadcasting: b treated as (1, 4) → (3, 4) +``` + +### Broadcast Resolution + +```csharp +public static (Shape, Shape) Broadcast(Shape left, Shape right) +{ + // Rules: + // 1. Align shapes from the right + // 2. Dimensions must be equal OR one must be 1 + // 3. Dimension of 1 is "stretched" to match +} +``` + +### MultiIterator + +For element-wise operations with broadcasting: + +```csharp +public static class MultiIterator +{ + // Creates paired iterators with broadcasting + public static (NDIterator, NDIterator) GetIterators( + UnmanagedStorage lhs, + UnmanagedStorage rhs, + bool broadcast); + + // Assignment with broadcasting + public static void Assign(NDArray lhs, NDArray rhs); +} +``` + +--- + +## Iterator System + +### NDIterator + +The iterator system handles traversal of arrays with different memory layouts: + +```csharp +public class NDIterator where T : unmanaged +{ + public Func MoveNext; // Get next value + public MoveNextReferencedDelegate MoveNextReference; // Get reference + public Func HasNext; // Check if more elements + public Action Reset; // Reset to beginning + + public bool AutoReset; // For broadcasting (smaller array loops) + public IteratorType Type; // Scalar, Vector, Matrix, Tensor +} +``` + +### Iterator Types + +```csharp +public enum IteratorType +{ + Scalar, // Single element + Vector, // 1D array + Matrix, // 2D array + Tensor // 3D+ array +} +``` + +### Optimization Paths + +The iterator chooses different code paths based on: + +1. **Contiguous arrays**: Direct pointer increment +2. **Sliced arrays**: Coordinate-to-offset calculation +3. **Auto-reset mode**: For broadcasting smaller arrays + +```csharp +// Contiguous: fast path +MoveNext = () => *((T*)Address + index++); + +// Sliced: uses shape.GetOffset +MoveNext = () => *((T*)Address + shape.GetOffset(index++)); +``` + +--- + +## Code Generation + +### Regen Templating + +NumSharp uses Regen (a custom templating engine) to generate type-specific code. This results in approximately **200,000 lines of generated code**. + +The pattern appears in many files: + +```csharp +#if _REGEN + #region Compute + switch (typeCode) + { + %foreach supported_dtypes,supported_dtypes_lowercase% + case NPTypeCode.#1: return DoOperation<#2>(arr); + % + default: + throw new NotSupportedException(); + } + #endregion +#else + // Generated code follows... + switch (typeCode) + { + case NPTypeCode.Boolean: return DoOperation(arr); + case NPTypeCode.Byte: return DoOperation(arr); + case NPTypeCode.Int16: return DoOperation(arr); + // ... all 12 types + } +#endif +``` + +### Why Code Generation? + +- **Performance**: Avoids boxing and virtual dispatch +- **Type safety**: Compile-time checks for each type +- **NumPy compatibility**: Exact type handling behavior + +### Trade-offs + +- **Heavy codebase**: 200K lines of generated code +- **Maintenance burden**: Changes require regeneration +- **Compile time**: Longer builds + +> **Note**: Migration to T4 templates or C# source generators is possible but not currently prioritized. + +--- + +## Development Workflow + +### Adding a New np.* Function + +1. **Research NumPy behavior**: + - Read NumPy documentation + - Run actual Python/NumPy code + - Document edge cases (NaN, empty arrays, broadcasting) + +2. **Choose implementation pattern**: + - If needs low-level optimization → Add to `DefaultEngine` + - If can compose existing functions → Implement directly in `np.*` + +3. **Implement**: + ```csharp + // In np.newfunction.cs + public static partial class np + { + public static NDArray newfunction(NDArray a, int axis = -1) + { + // Implementation + } + } + ``` + +4. **Write tests**: + - Run NumPy code, capture exact outputs + - Replicate 1-to-1 in C# tests + - Include edge cases + +### Testing Philosophy + +Tests should be based on **actual NumPy execution**: + +```python +# Python +import numpy as np +a = np.array([1, 2, np.nan, 4]) +result = np.nanmean(a) +print(result) # 2.3333... +``` + +```csharp +// C# test +[TestMethod] +public void nanmean_WithNaN_IgnoresNaN() +{ + var a = np.array(new double[] { 1, 2, double.NaN, 4 }); + var result = np.nanmean(a); + Assert.AreEqual(2.333333, result.GetDouble(), 0.0001); +} +``` + +### Test Coverage + +- Tests use MSTest framework +- Many tests were adapted from NumPy's own test suite +- Coverage is decent but has gaps in edge cases + +--- + +## Implemented Capabilities + +NumSharp provides extensive NumPy-compatible functionality across multiple domains: + +### Array Creation +`np.array`, `np.zeros`, `np.ones`, `np.empty`, `np.full`, `np.arange`, `np.linspace`, `np.eye`, `np.meshgrid`, `np.mgrid`, `np.copy`, `np.asarray`, `np.frombuffer`, `np.zeros_like`, `np.ones_like`, `np.empty_like`, `np.full_like` + +### Stacking & Joining +`np.concatenate`, `np.stack`, `np.hstack`, `np.vstack`, `np.dstack` + +### Broadcasting +`np.broadcast`, `np.broadcast_to`, `np.broadcast_arrays`, `np.are_broadcastable` + +### Mathematical Functions +`np.sum`, `np.prod`, `np.cumsum`, `np.power`, `np.sqrt`, `np.abs`, `np.sign`, `np.floor`, `np.ceil`, `np.round`, `np.clip`, `np.modf`, `np.maximum`, `np.minimum`, `np.log`, `np.log2`, `np.log10`, `np.log1p`, `np.exp`, `np.exp2`, `np.expm1`, `np.sin`, `np.cos`, `np.tan` + +### Statistics +`np.mean`, `np.std`, `np.var`, `np.amax`, `np.amin`, `np.argmax`, `np.argmin` + +### Sorting & Searching +`np.argsort`, `np.searchsorted` + +### Linear Algebra +`np.dot`, `np.matmul`, `np.outer`, `np.linalg.norm`, `nd.inv()`, `nd.qr()`, `nd.svd()`, `nd.lstsq()`, `nd.multi_dot()`, `nd.matrix_power()` + +### Shape Manipulation +`np.reshape`, `np.transpose`, `np.ravel`, `np.squeeze`, `np.expand_dims`, `np.swapaxes`, `np.moveaxis`, `np.rollaxis`, `np.atleast_1d/2d/3d`, `np.unique`, `np.repeat`, `np.copyto`, `nd.flatten()`, `nd.roll()`, `nd.delete()` + +### Logic Functions +`np.all`, `np.any`, `np.allclose`, `np.array_equal`, `np.isnan`, `np.isinf`, `np.isfinite`, `np.find_common_type` + +### Operators +- Arithmetic: `+`, `-`, `*`, `/`, `%`, unary `-` +- Comparison: `==`, `!=`, `>`, `>=`, `<`, `<=` +- Logical: `&`, `|`, `!` + +### Indexing & Selection +Integer indexing, string slice notation, Slice objects, boolean masking, fancy indexing (NDArray indices), `np.nonzero` + +### Random Sampling +`np.random.rand`, `np.random.randn`, `np.random.randint`, `np.random.uniform`, `np.random.choice`, `np.random.shuffle`, `np.random.permutation`, `np.random.beta`, `np.random.binomial`, `np.random.gamma`, `np.random.poisson`, `np.random.exponential`, `np.random.geometric`, `np.random.lognormal`, `np.random.chisquare`, `np.random.bernoulli` + +### File I/O +`np.save` (`.npy`), `np.load` (`.npy`, `.npz`), `np.fromfile`, `nd.tofile()` + +--- + +## Future Roadmap + +### Near-term Goals + +1. **NumPy 2.x API Mapping**: Comprehensive audit of: + - Existing functions + - Missing functions + - Behavioral discrepancies + +2. **Behavioral Corrections**: Fix implementations that diverge from NumPy + +3. **Documentation**: API documentation and examples + +### Potential Future Directions + +1. **Alternative Backends**: GPU (CUDA), SIMD intrinsics, MKL/BLAS +2. **Source Generator Migration**: Replace Regen with C# source generators +3. **Span/Memory Integration**: Where beneficial without breaking changes + +### Breaking Changes + +The library accepts breaking changes - it was deprecated for an extended period and is being revitalized. API stability is not a constraint. + +--- + +## Appendix: Key Files Reference + +| Component | Primary Files | +|-----------|--------------| +| NDArray | `Backends/NDArray.cs`, `Backends/NDArray.*.cs` | +| Storage | `Backends/Unmanaged/UnmanagedStorage.cs` | +| Shape | `View/Shape.cs` | +| Slicing | `View/Slice.cs` | +| TensorEngine | `Backends/TensorEngine.cs`, `Backends/Default/DefaultEngine.*.cs` | +| Iterators | `Backends/Iterators/NDIterator.cs`, `MultiIterator.cs` | +| np API | `APIs/np.cs`, individual `np.*.cs` files | +| Operators | `Operations/Elementwise/NDArray.Primitive.cs` | +| Type Info | `Utilities/InfoOf.cs`, `Backends/NPTypeCode.cs` | +| Random | `RandomSampling/np.random.cs`, `NumPyRandom.cs` | +| Generic | `Generics/NDArray\`1.cs` | + +--- + +## Contributing + +When contributing to NumSharp: + +1. **Match NumPy exactly** - Run Python code, observe behavior, replicate +2. **Write tests first** - Based on actual NumPy output +3. **Handle all types** - Use Regen patterns or switch statements for all 12 dtypes +4. **Consider edge cases** - NaN, empty arrays, scalar vs array, broadcasting +5. **Document behavior** - Reference NumPy docs in comments + +See the test suite for examples of expected behavior patterns. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..d3386d360 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,531 @@ +# Contributing to NumSharp + +This guide covers the practical aspects of contributing to NumSharp. + +## Development Philosophy + +### The Golden Rule: Match NumPy Exactly + +NumSharp aims for **1-to-1 behavioral compatibility** with NumPy 2.x. This means: + +1. **Run actual NumPy code first** +2. **Observe and document the exact output** +3. **Replicate that behavior precisely in C#** + +```python +# Step 1: Run in Python +import numpy as np + +a = np.array([[1, 2], [3, 4]]) +b = np.sum(a, axis=0, keepdims=True) +print(b) # [[4 6]] +print(b.shape) # (1, 2) +print(b.dtype) # int64 +``` + +```csharp +// Step 2: Match in C# +[TestMethod] +public void sum_Axis0_Keepdims() +{ + var a = np.array(new int[,] { { 1, 2 }, { 3, 4 } }); + var b = np.sum(a, axis: 0, keepdims: true); + + Assert.AreEqual("[[4, 6]]", b.ToString()); + CollectionAssert.AreEqual(new[] { 1, 2 }, b.shape); + Assert.AreEqual(typeof(int), b.dtype); +} +``` + +### Edge Cases Matter + +NumPy has specific behavior for edge cases. Always test: + +- Empty arrays: `np.array([])` +- Scalars vs 0-d arrays: `np.array(5)` vs `5` +- NaN/Inf handling: `np.array([1, np.nan, 3])` +- Type promotion: `np.array([1, 2]) + np.array([1.5])` → float64 +- Broadcasting edge cases: shapes like `(3, 1)` + `(1, 4)` +- Negative axis values: `axis=-1` +- Out-of-bounds: What errors does NumPy raise? + +### Breaking Changes Are Acceptable + +We accept breaking changes to align with NumPy 2.x behavior. + +--- + +## Project Structure for Contributors + +### Where to Add New Functions + +| Category | Location | Example | +|----------|----------|---------| +| Creation | `src/NumSharp.Core/Creation/` | np.zeros, np.arange, np.concatenate | +| Math | `src/NumSharp.Core/Math/` | np.sum, np.sin, np.power | +| Statistics | `src/NumSharp.Core/Statistics/` | np.mean, np.std, np.var | +| Logic | `src/NumSharp.Core/Logic/` | np.all, np.any, np.allclose | +| Manipulation | `src/NumSharp.Core/Manipulation/` | np.reshape, np.transpose, np.squeeze | +| LinearAlgebra | `src/NumSharp.Core/LinearAlgebra/` | np.dot, np.matmul, np.linalg.* | +| Selection | `src/NumSharp.Core/Selection/` | Indexing, masking | +| Indexing | `src/NumSharp.Core/Indexing/` | np.nonzero | +| Sorting | `src/NumSharp.Core/Sorting_Searching_Counting/` | np.argsort, np.argmax | +| Random | `src/NumSharp.Core/RandomSampling/` | np.random.* | +| File I/O | `src/NumSharp.Core/APIs/` | np.save, np.load | + +### File Naming Convention + +``` +np.{function_name}.cs +``` + +Examples: +- `np.sum.cs` +- `np.arange.cs` +- `np.concatenate.cs` + +### Test Location + +``` +test/NumSharp.UnitTest/{Category}/{FunctionName}Tests.cs +``` + +--- + +## Implementation Patterns + +### Pattern 1: Simple np.* Function (Composing Others) + +When the function can be built from existing operations: + +```csharp +// File: src/NumSharp.Core/Statistics/np.std.cs +namespace NumSharp +{ + public static partial class np + { + /// + /// Compute the standard deviation along the specified axis. + /// + /// https://numpy.org/doc/stable/reference/generated/numpy.std.html + public static NDArray std(NDArray a, int? axis = null, NPTypeCode? dtype = null, bool keepdims = false) + { + var mean_val = np.mean(a, axis: axis, keepdims: true); + var diff = a - mean_val; + var sq = np.power(diff, 2); + var variance = np.mean(sq, axis: axis, keepdims: keepdims); + return np.sqrt(variance); + } + } +} +``` + +### Pattern 2: Function Requiring TensorEngine + +When you need low-level type-specific optimization: + +**Step 1: Add to TensorEngine abstract class** +```csharp +// Backends/TensorEngine.cs +public abstract NDArray NewOperation(in NDArray nd, int? axis = null); +``` + +**Step 2: Implement in DefaultEngine** +```csharp +// Backends/Default/DefaultEngine.NewOperation.cs +public partial class DefaultEngine +{ + public override NDArray NewOperation(in NDArray nd, int? axis = null) + { + // Type switch using Regen pattern + switch (nd.GetTypeCode) + { + case NPTypeCode.Double: return NewOperation_Double(nd, axis); + case NPTypeCode.Single: return NewOperation_Single(nd, axis); + // ... all types + } + } + + private NDArray NewOperation_Double(in NDArray nd, int? axis) + { + // Actual implementation + } +} +``` + +**Step 3: Add np.* wrapper** +```csharp +// Math/np.newoperation.cs +public static partial class np +{ + public static NDArray newoperation(NDArray a, int? axis = null) + { + return a.TensorEngine.NewOperation(a, axis); + } +} +``` + +### Pattern 3: Type Switch (Regen Style) + +For operations that need type-specific code: + +```csharp +#if _REGEN + switch (arr.typecode) + { + %foreach supported_dtypes,supported_dtypes_lowercase% + case NPTypeCode.#1: return Process<#2>(arr); + % + default: + throw new NotSupportedException(); + } +#else + switch (arr.typecode) + { + case NPTypeCode.Boolean: return Process(arr); + case NPTypeCode.Byte: return Process(arr); + case NPTypeCode.Int16: return Process(arr); + case NPTypeCode.UInt16: return Process(arr); + case NPTypeCode.Int32: return Process(arr); + case NPTypeCode.UInt32: return Process(arr); + case NPTypeCode.Int64: return Process(arr); + case NPTypeCode.UInt64: return Process(arr); + case NPTypeCode.Char: return Process(arr); + case NPTypeCode.Double: return Process(arr); + case NPTypeCode.Single: return Process(arr); + case NPTypeCode.Decimal: return Process(arr); + default: + throw new NotSupportedException(); + } +#endif +``` + +--- + +## Working with Core Types + +### Creating NDArrays in Code + +```csharp +// From shape (zeros) +var a = new NDArray(NPTypeCode.Double, new Shape(3, 4)); + +// From .NET array +var b = new NDArray(new double[] { 1, 2, 3, 4 }); +var c = new NDArray(new double[,] { { 1, 2 }, { 3, 4 } }); + +// Using np.* functions +var d = np.zeros(3, 4); +var e = np.arange(10); +var f = np.array(new[] { 1.0, 2.0, 3.0 }); +``` + +### Accessing Data + +```csharp +// Typed access (preferred for performance) +unsafe +{ + double* ptr = (double*)nd.Address; + for (int i = 0; i < nd.size; i++) + ptr[i] = i * 2.0; +} + +// Via ArraySlice +ArraySlice slice = nd.Storage.GetData(); +Span span = slice.AsSpan(); + +// Via iterator (handles sliced arrays correctly) +using var iter = new NDIterator(nd); +while (iter.HasNext()) +{ + double val = iter.MoveNext(); +} +``` + +### Working with Shape + +```csharp +// Create shape +Shape shape = new Shape(3, 4, 5); +Shape scalar = Shape.Scalar; +Shape vector = new Shape(10); + +// Properties +int ndim = shape.NDim; // 3 +int size = shape.size; // 60 +int[] dims = shape.Dimensions; // [3, 4, 5] +int[] strides = shape.Strides; // [20, 5, 1] + +// Checks +bool isScalar = shape.IsScalar; +bool isContiguous = shape.IsContiguous; +bool isSliced = shape.IsSliced; + +// Coordinate ↔ Offset +int offset = shape.GetOffset(1, 2, 3); // Linear index +int[] coords = shape.GetCoordinates(offset); +``` + +### Working with Slices + +```csharp +// Parse string notation +Slice[] slices = Slice.ParseSlices("1:3, :, -1"); + +// Programmatic slicing +var slice = new Slice(start: 1, stop: 5, step: 2); +var index = Slice.Index(3); // Single element + +// Apply to NDArray +NDArray view = nd[slices]; +NDArray view2 = nd[1, Slice.All, Slice.Index(-1)]; +``` + +--- + +## Testing Guidelines + +### Test File Structure + +```csharp +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NumSharp; + +namespace NumSharp.UnitTest.Math +{ + [TestClass] + public class np_sum_Tests + { + [TestMethod] + public void sum_1DArray_ReturnsScalar() + { + // Arrange + var a = np.array(new[] { 1, 2, 3, 4, 5 }); + + // Act + var result = np.sum(a); + + // Assert + Assert.AreEqual(15, result.GetInt32()); + } + + [TestMethod] + public void sum_2DArray_Axis0_SumsColumns() + { + var a = np.array(new int[,] { { 1, 2 }, { 3, 4 } }); + var result = np.sum(a, axis: 0); + + CollectionAssert.AreEqual(new[] { 4, 6 }, result.ToArray()); + } + + [TestMethod] + public void sum_EmptyArray_ReturnsZero() + { + var a = np.array(new double[0]); + var result = np.sum(a); + + Assert.AreEqual(0.0, result.GetDouble()); + } + + [TestMethod] + public void sum_WithNaN_PropagatesNaN() + { + var a = np.array(new[] { 1.0, double.NaN, 3.0 }); + var result = np.sum(a); + + Assert.IsTrue(double.IsNaN(result.GetDouble())); + } + } +} +``` + +### What to Test + +1. **Basic functionality** - Normal use case +2. **All relevant dtypes** - int, float, double at minimum +3. **Different shapes** - 1D, 2D, 3D+ +4. **Axis parameter** - All valid axes, negative axes +5. **keepdims parameter** - true and false +6. **Edge cases**: + - Empty arrays + - Scalar inputs + - Single-element arrays + - NaN/Inf values + - Type promotion + +### Running Tests + +```bash +cd test/NumSharp.UnitTest +dotnet test +``` + +--- + +## Performance Considerations + +### Contiguous vs Sliced Arrays + +```csharp +// Fast path: contiguous memory +if (nd.Shape.IsContiguous) +{ + unsafe + { + double* ptr = (double*)nd.Address; + // Direct pointer arithmetic + } +} +else +{ + // Slow path: use iterator or shape.GetOffset + using var iter = new NDIterator(nd); + // ... +} +``` + +### Parallel Threshold + +DefaultEngine uses `Parallel.For` for arrays > 85,000 elements: + +```csharp +public const int ParallelAbove = 84999; + +if (size > ParallelAbove) +{ + Parallel.For(0, size, i => { /* ... */ }); +} +else +{ + for (int i = 0; i < size; i++) { /* ... */ } +} +``` + +### Avoid Allocations in Hot Paths + +```csharp +// Bad: allocates array each call +int[] indices = new int[ndim]; + +// Good: stackalloc for small, fixed-size +Span indices = stackalloc int[ndim]; + +// Good: reuse iterator +using var iter = new NDIterator(nd); +var moveNext = iter.MoveNext; // Cache delegate +while (iter.HasNext()) + moveNext(); +``` + +--- + +## Common Pitfalls + +### 1. Forgetting View Semantics (CRITICAL) + +```csharp +// WRONG: modifies original +var view = original["1:3"]; +view[0] = 999; // original[1] is now 999! + +// RIGHT: explicit copy if needed +var copy = original["1:3"].copy(); +copy[0] = 999; // original unchanged +``` + +### 2. Type Assumptions + +```csharp +// WRONG: assumes type +double val = nd.GetDouble(); // Throws if not double + +// RIGHT: check or convert +if (nd.dtype == typeof(double)) + double val = nd.GetDouble(); +// OR +var converted = nd.astype(np.float64); +``` + +### 3. Axis Handling + +```csharp +// WRONG: forgetting negative axis +if (axis >= nd.ndim) throw new ArgumentOutOfRangeException(); + +// RIGHT: normalize negative axis first +if (axis < 0) axis = nd.ndim + axis; +if (axis < 0 || axis >= nd.ndim) throw new ArgumentOutOfRangeException(); +``` + +### 4. Empty Array Edge Cases + +```csharp +// WRONG: crashes on empty +var result = arr[0]; // IndexOutOfRange if arr.size == 0 + +// RIGHT: check first +if (arr.size == 0) + return np.array(Array.Empty()); +``` + +--- + +## Documentation Standards + +### XML Documentation + +```csharp +/// +/// Return the sum of array elements over a given axis. +/// +/// Input array. +/// Axis or axes along which a sum is performed. +/// The default, axis=None, will sum all of the elements of the input array. +/// The type of the returned array and of the accumulator +/// in which the elements are summed. +/// If this is set to True, the axes which are reduced +/// are left in the result as dimensions with size one. +/// An array with the same shape as a, with the specified axis removed. +/// https://numpy.org/doc/stable/reference/generated/numpy.sum.html +public static NDArray sum(NDArray a, int? axis = null, NPTypeCode? dtype = null, bool keepdims = false) +``` + +### Always Include NumPy Reference Link + +```csharp +/// https://numpy.org/doc/stable/reference/generated/numpy.{function}.html +``` + +--- + +## Supported Types Reference + +All operations must handle these 12 types: + +| NPTypeCode | C# Type | NPTypeCode | C# Type | +|------------|---------|------------|---------| +| Boolean | bool | Int64 | long | +| Byte | byte | UInt64 | ulong | +| Int16 | short | Char | char | +| UInt16 | ushort | Single | float | +| Int32 | int | Double | double | +| UInt32 | uint | Decimal | decimal | + +Use `InfoOf` for type information without reflection: +```csharp +var size = InfoOf.Size; // 8 +var code = InfoOf.NPTypeCode; // NPTypeCode.Single +var zero = InfoOf.Zero; // 0 +``` + +--- + +## Questions? + +If you're unsure about implementation details: + +1. Check existing similar functions in the codebase +2. Run the Python equivalent and document behavior +3. Look at NumPy source code for complex edge cases +4. Open an issue for discussion before large changes diff --git a/docs/issues/categories.md b/docs/issues/categories.md new file mode 100644 index 000000000..56e88efa9 --- /dev/null +++ b/docs/issues/categories.md @@ -0,0 +1,332 @@ +# NumSharp Open Issues - Categorized + +**Total open issues: 142** + +| Category | Count | +|----------|-------| +| [np.* Bugs](#np-bugs) | 29 | +| [Other Bugs](#other-bugs) | 28 | +| [Missing np.* Functions](#missing-np-functions) | 34 | +| [Feature Requests](#feature-requests) | 21 | +| [Performance Issues](#performance-issues) | 4 | +| [Questions / How-To / Off-Topic](#questions--how-to--off-topic) | 27 | + +--- + +## np.* Bugs + +Bugs in existing `np.*` functions: wrong results, crashes, or API typos. + +| Issue | Title | Author | Labels | +|-------|-------|--------|--------| +| [#315](https://github.com/SciSharp/NumSharp/issues/315) | ToString should truncate its output | @thomasd3 | bug, enhancement | +| [#362](https://github.com/SciSharp/NumSharp/issues/362) | Implicit operators for >, >=, <, <= | @deepakkumar1984 | help wanted, missing feature/s | +| [#398](https://github.com/SciSharp/NumSharp/issues/398) | Typo in library np.random.stardard | @QadiymStewart | | +| [#405](https://github.com/SciSharp/NumSharp/issues/405) | np.argsort not sorting properly | @tk4218 | | +| [#407](https://github.com/SciSharp/NumSharp/issues/407) | np.negative is not working ? | @LordTrololo | | +| [#408](https://github.com/SciSharp/NumSharp/issues/408) | np.meshgrid() has a hidden error returning wrong results | @LordTrololo | | +| [#418](https://github.com/SciSharp/NumSharp/issues/418) | help me | @mak27arr | | +| [#419](https://github.com/SciSharp/NumSharp/issues/419) | np.meshgrid error | @mak27arr | | +| [#426](https://github.com/SciSharp/NumSharp/issues/426) | arctan2() returning incorrect value | @RoseberryPi | | +| [#428](https://github.com/SciSharp/NumSharp/issues/428) | Typo in NDArray.ToMuliArray method name | @jpmn | | +| [#436](https://github.com/SciSharp/NumSharp/issues/436) | np.searchsorted error! | @wangfeixing | | +| [#437](https://github.com/SciSharp/NumSharp/issues/437) | argmin is not the same with numpy | @tomachristian | | +| [#443](https://github.com/SciSharp/NumSharp/issues/443) | 0.3.0 from NuGet throwing NotSupportedException on negate function call | @gandalfh | | +| [#447](https://github.com/SciSharp/NumSharp/issues/447) | np.sum() Is supported on numsharp0.20.5, but not on NumSharp0.30.0 | @lijianxin520 | | +| [#452](https://github.com/SciSharp/NumSharp/issues/452) | [missing feature/s] NumSharp's np.around() method is missing decimals parameter which is available in NumPy | @shashi4u | | +| [#456](https://github.com/SciSharp/NumSharp/issues/456) | silent catastrophe in implicit casting singleton array to value type | @dmacd | | +| [#461](https://github.com/SciSharp/NumSharp/issues/461) | np.save incorrectly saves System.Byte arrays as signed | @rikkitook | | +| [#466](https://github.com/SciSharp/NumSharp/issues/466) | [Bug] np.random.choice raise Exception | @QingtaoLi1 | | +| [#468](https://github.com/SciSharp/NumSharp/issues/468) | np_array.convolve returning Null | @dklein9500 | | +| [#470](https://github.com/SciSharp/NumSharp/issues/470) | Numsharp0.30.0 np.random.choice() method missing cause Exception | @UCtreespring | | +| [#477](https://github.com/SciSharp/NumSharp/issues/477) | Different Result between NumPy and NumSharp with np.matmul Function | @Koyamin | | +| [#487](https://github.com/SciSharp/NumSharp/issues/487) | linspace to Array as type float, while other functions as type double | @changjian-github | | +| [#488](https://github.com/SciSharp/NumSharp/issues/488) | np.random.choice raised System.NotSupportedException | @alvinfebriando | | +| [#490](https://github.com/SciSharp/NumSharp/issues/490) | np.random.choice with replace: false produces duplicates | @GThibeault | | +| [#499](https://github.com/SciSharp/NumSharp/issues/499) | Possible typo "ToMuliDimArray()" | @sappho192 | | +| [#505](https://github.com/SciSharp/NumSharp/issues/505) | `np.convolve` return null exception | @behroozbc | | +| [#507](https://github.com/SciSharp/NumSharp/issues/507) | np.maximum error | @Thanatos0173 | | +| [#508](https://github.com/SciSharp/NumSharp/issues/508) | np.hstack has diffrent effect from python | @xdqa01 | | +| [#517](https://github.com/SciSharp/NumSharp/issues/517) | Error when loading a `.npy` file containing a scalar value | @thalesfm | | + +### Breakdown + +**Wrong/Unexpected Results:** +- [#398](https://github.com/SciSharp/NumSharp/issues/398) - Typo: np.random.stardard_normal (should be standard) +- [#405](https://github.com/SciSharp/NumSharp/issues/405) - np.argsort returns wrong order +- [#408](https://github.com/SciSharp/NumSharp/issues/408) - np.meshgrid returns wrong results (hidden memory bug) +- [#426](https://github.com/SciSharp/NumSharp/issues/426) - np.arctan2 returns incorrect value +- [#437](https://github.com/SciSharp/NumSharp/issues/437) - np.argmin behavior differs from NumPy +- [#456](https://github.com/SciSharp/NumSharp/issues/456) - Implicit cast of singleton NDArray silently produces wrong value (memory reinterpretation) +- [#466](https://github.com/SciSharp/NumSharp/issues/466) - np.random.choice raises NotSupportedException +- [#470](https://github.com/SciSharp/NumSharp/issues/470) - np.random.choice() throws NotSupportedException (0.30.0) +- [#477](https://github.com/SciSharp/NumSharp/issues/477) - np.matmul returns different results from NumPy for 3D arrays +- [#488](https://github.com/SciSharp/NumSharp/issues/488) - np.random.choice raises NotSupportedException +- [#490](https://github.com/SciSharp/NumSharp/issues/490) - np.random.choice with replace=false produces duplicates +- [#507](https://github.com/SciSharp/NumSharp/issues/507) - np.maximum throws random errors during repeated calls +- [#508](https://github.com/SciSharp/NumSharp/issues/508) - np.hstack produces different results from Python + +**Crashes / Exceptions:** +- [#362](https://github.com/SciSharp/NumSharp/issues/362) - Comparison operators >, >=, <, <= return null +- [#418](https://github.com/SciSharp/NumSharp/issues/418) - np.meshgrid second return value is always null +- [#419](https://github.com/SciSharp/NumSharp/issues/419) - np.meshgrid second return value crashes the program +- [#436](https://github.com/SciSharp/NumSharp/issues/436) - np.searchsorted throws error on double arrays +- [#443](https://github.com/SciSharp/NumSharp/issues/443) - np.negate throws NotSupportedException on 0.30.0 +- [#447](https://github.com/SciSharp/NumSharp/issues/447) - np.sum throws NotSupportedException on 0.30.0 +- [#466](https://github.com/SciSharp/NumSharp/issues/466) - np.random.choice raises NotSupportedException +- [#468](https://github.com/SciSharp/NumSharp/issues/468) - NDArray.convolve() returns null +- [#470](https://github.com/SciSharp/NumSharp/issues/470) - np.random.choice() throws NotSupportedException (0.30.0) +- [#488](https://github.com/SciSharp/NumSharp/issues/488) - np.random.choice raises NotSupportedException +- [#505](https://github.com/SciSharp/NumSharp/issues/505) - np.convolve returns null / NullReferenceException +- [#507](https://github.com/SciSharp/NumSharp/issues/507) - np.maximum throws random errors during repeated calls +- [#517](https://github.com/SciSharp/NumSharp/issues/517) - np.load fails on scalar .npy files (off-by-one in header parsing) + +**API Typos / Naming:** +- [#398](https://github.com/SciSharp/NumSharp/issues/398) - Typo: np.random.stardard_normal (should be standard) +- [#428](https://github.com/SciSharp/NumSharp/issues/428) - Typo: NDArray.ToMuliArray (should be ToMultiArray) +- [#452](https://github.com/SciSharp/NumSharp/issues/452) - np.around() missing decimals parameter +- [#499](https://github.com/SciSharp/NumSharp/issues/499) - Typo: ToMuliDimArray() should be ToMultiDimArray() + +**Version 0.30.0 Regressions:** +- [#443](https://github.com/SciSharp/NumSharp/issues/443) - np.negate throws NotSupportedException on 0.30.0 +- [#447](https://github.com/SciSharp/NumSharp/issues/447) - np.sum throws NotSupportedException on 0.30.0 +- [#466](https://github.com/SciSharp/NumSharp/issues/466) - np.random.choice raises NotSupportedException +- [#470](https://github.com/SciSharp/NumSharp/issues/470) - np.random.choice() throws NotSupportedException (0.30.0) +- [#488](https://github.com/SciSharp/NumSharp/issues/488) - np.random.choice raises NotSupportedException + +--- + +## Other Bugs + +Bugs in core infrastructure, indexing, memory management, platform compatibility. + +| Issue | Title | Author | Labels | +|-------|-------|--------|--------| +| [#366](https://github.com/SciSharp/NumSharp/issues/366) | Masking (ndarray[nd]) | @henon | | +| [#368](https://github.com/SciSharp/NumSharp/issues/368) | Masking a slice ("...") returns null | @ohjerm | | +| [#369](https://github.com/SciSharp/NumSharp/issues/369) | Slicing NotSupportedException | @Oceania2018 | missing feature/s | +| [#396](https://github.com/SciSharp/NumSharp/issues/396) | Bitmap.ToNDArray problem with odd bitmap width | @herrvonregen | | +| [#410](https://github.com/SciSharp/NumSharp/issues/410) | np.save fails with IndexOutOfRangeException for jagged arrays | @Jmerk523 | | +| [#412](https://github.com/SciSharp/NumSharp/issues/412) | The type 'NDArray' exists in both 'NumSharp.Core, Version=0.20.5.0, ' and 'NumSharp.Lite, Version=0.1.7.0, | @sportbilly21 | | +| [#422](https://github.com/SciSharp/NumSharp/issues/422) | Index of element with a condiction | @EnricoBeltramo | | +| [#423](https://github.com/SciSharp/NumSharp/issues/423) | "System.NotImplementedException: '' --> someArray = np.frombuffer(byteBuffer.ToArray(), np.uint32); | @mehmetcanbalci-Notrino | | +| [#430](https://github.com/SciSharp/NumSharp/issues/430) | NumSharp.Backends.Unmanaged.UnmanagedMemoryBlock`1 fails on Mono on Linux | @kgoderis | | +| [#433](https://github.com/SciSharp/NumSharp/issues/433) | NDArray exists in both NumSharp.Core, Version=0.20.5.0 and NumSharp.Lite, Version=0.1.9.0 | @gscheck | | +| [#434](https://github.com/SciSharp/NumSharp/issues/434) | AccessViolationException when selecting indexes using ndarray[ndarray] and setting a scalar value | @lijianxin520 | bug | +| [#440](https://github.com/SciSharp/NumSharp/issues/440) | NDArray.ToBitmap() has critical issue with 24bpp VERTICAL images | @MiroslavKabat | bug | +| [#448](https://github.com/SciSharp/NumSharp/issues/448) | Debug.Assert(...) causes tests to stop the entire process | @Nucs | bug | +| [#455](https://github.com/SciSharp/NumSharp/issues/455) | NumSharp does not allow building with IL2CPP via Unity | @julia-koziel | | +| [#467](https://github.com/SciSharp/NumSharp/issues/467) | NumSharp and Tensorflow.NET works on Desktop but fails on Cloud Web Service (.NET 5) | @marsousi | | +| [#471](https://github.com/SciSharp/NumSharp/issues/471) | Unhandled Exception: System.NotSupportedException: Specified method is not supported. | @KonardAdams | | +| [#475](https://github.com/SciSharp/NumSharp/issues/475) | ToBitmap fails if not contiguous because of Broadcast mismatch | @ponzis | | +| [#476](https://github.com/SciSharp/NumSharp/issues/476) | Numsharp.Core contains many Debug.Assert() lines | @rtwalterson | | +| [#484](https://github.com/SciSharp/NumSharp/issues/484) | np.load System.Exception | @Kiord | | +| [#491](https://github.com/SciSharp/NumSharp/issues/491) | ToBitmap() - datatype mistmatch | @davidvct | | +| [#492](https://github.com/SciSharp/NumSharp/issues/492) | critical vulnerability in version 5.0.2 of system.drawing.common | @jkl-ds | | +| [#493](https://github.com/SciSharp/NumSharp/issues/493) | Numsharp array output in .net interactive notebooks is misleading | @oxygen-dioxide | | +| [#501](https://github.com/SciSharp/NumSharp/issues/501) | Memory leak? | @TakuNishiumi | | +| [#506](https://github.com/SciSharp/NumSharp/issues/506) | Cannot create an NDArray of shorts | @NickBotelho | | +| [#509](https://github.com/SciSharp/NumSharp/issues/509) | Extremely poor performance on sum reduce | @lucdem | | +| [#514](https://github.com/SciSharp/NumSharp/issues/514) | SetItem for multiple Ids not working | @MaxOmlor | | +| [#519](https://github.com/SciSharp/NumSharp/issues/519) | BUG: NDArray filted_array = ori_array[max_prob > conf_threshold]; | @1Zengy | | +| [#520](https://github.com/SciSharp/NumSharp/issues/520) | Can't convert to Vector3 | @xiaoshux | | + +### Breakdown + +**Indexing / Slicing / Masking:** +- [#366](https://github.com/SciSharp/NumSharp/issues/366) - Boolean masking ndarray[nd] throws NotSupportedException on setter +- [#368](https://github.com/SciSharp/NumSharp/issues/368) - Masking a slice ('...') returns null +- [#410](https://github.com/SciSharp/NumSharp/issues/410) - np.save fails with IndexOutOfRangeException for jagged arrays +- [#422](https://github.com/SciSharp/NumSharp/issues/422) - Boolean indexing / conditional filtering not working +- [#434](https://github.com/SciSharp/NumSharp/issues/434) - AccessViolationException on ndarray[ndarray] index + scalar set +- [#519](https://github.com/SciSharp/NumSharp/issues/519) - Boolean filtering randomly returns correct results or empty array + +**Memory / Crashes:** +- [#430](https://github.com/SciSharp/NumSharp/issues/430) - UnmanagedMemoryBlock fails on Mono/Linux +- [#434](https://github.com/SciSharp/NumSharp/issues/434) - AccessViolationException on ndarray[ndarray] index + scalar set +- [#501](https://github.com/SciSharp/NumSharp/issues/501) - Memory leak: repeated NDArray operations consume excessive memory + +**Platform / Compatibility:** +- [#430](https://github.com/SciSharp/NumSharp/issues/430) - UnmanagedMemoryBlock fails on Mono/Linux +- [#455](https://github.com/SciSharp/NumSharp/issues/455) - IL2CPP build fails in Unity (LAPACKProviderType) +- [#467](https://github.com/SciSharp/NumSharp/issues/467) - Fails on Azure Web Service / cloud (DllNotFoundException tensorflow) +- [#492](https://github.com/SciSharp/NumSharp/issues/492) - Critical vulnerability in System.Drawing.Common 5.0.2 +- [#520](https://github.com/SciSharp/NumSharp/issues/520) - Cannot cast NDArray values to Unity Vector3 (implicit cast bug) + +**Bitmap / Image:** +- [#396](https://github.com/SciSharp/NumSharp/issues/396) - Bitmap.ToNDArray fails with odd bitmap widths (stride alignment) +- [#440](https://github.com/SciSharp/NumSharp/issues/440) - NDArray.ToBitmap() incorrect for 24bpp vertical images +- [#475](https://github.com/SciSharp/NumSharp/issues/475) - ToBitmap fails on non-contiguous arrays (broadcast mismatch) +- [#491](https://github.com/SciSharp/NumSharp/issues/491) - ToBitmap() type mismatch (int arrays not supported) +- [#492](https://github.com/SciSharp/NumSharp/issues/492) - Critical vulnerability in System.Drawing.Common 5.0.2 + +--- + +## Missing np.* Functions + +Feature requests for specific NumPy API functions not yet implemented. + +| Issue | Function | Author | Labels | +|-------|----------|--------|--------| +| [#75](https://github.com/SciSharp/NumSharp/issues/75) | np.asarray | @Oceania2018 | enhancement | +| [#78](https://github.com/SciSharp/NumSharp/issues/78) | np.where | @Oceania2018 | enhancement | +| [#105](https://github.com/SciSharp/NumSharp/issues/105) | np.vdot | @Oceania2018 | enhancement | +| [#106](https://github.com/SciSharp/NumSharp/issues/106) | np.inner | @Oceania2018 | help wanted | +| [#108](https://github.com/SciSharp/NumSharp/issues/108) | np.tensordot | @Oceania2018 | help wanted | +| [#114](https://github.com/SciSharp/NumSharp/issues/114) | np.fft.fft | @Oceania2018 | enhancement | +| [#202](https://github.com/SciSharp/NumSharp/issues/202) | np.pad | @skywalkerisnull | enhancement | +| [#210](https://github.com/SciSharp/NumSharp/issues/210) | np.all (with axis support) | @Esther2013 | | +| [#220](https://github.com/SciSharp/NumSharp/issues/220) | np.flip | @pkingwsd | enhancement | +| [#221](https://github.com/SciSharp/NumSharp/issues/221) | np.rot90 | @pkingwsd | | +| [#239](https://github.com/SciSharp/NumSharp/issues/239) | np.linalg.norm (full implementation) | @henon | enhancement | +| [#298](https://github.com/SciSharp/NumSharp/issues/298) | np.random.choice (weighted sampling) | @Plankton555 | enhancement | +| [#360](https://github.com/SciSharp/NumSharp/issues/360) | np.any (with axis support) | @Oceania2018 | enhancement | +| [#365](https://github.com/SciSharp/NumSharp/issues/365) | np.nonzero | @Oceania2018 | enhancement | +| [#373](https://github.com/SciSharp/NumSharp/issues/373) | np.median | @turowicz | help wanted, missing feature/s | +| [#374](https://github.com/SciSharp/NumSharp/issues/374) | np.append | @solarflarefx | help wanted, missing feature/s | +| [#378](https://github.com/SciSharp/NumSharp/issues/378) | np.frombuffer | @Nucs | enhancement, missing feature/s | +| [#397](https://github.com/SciSharp/NumSharp/issues/397) | np.tile | @QadiymStewart | | +| [#413](https://github.com/SciSharp/NumSharp/issues/413) | np.split | @lqdev | | +| [#414](https://github.com/SciSharp/NumSharp/issues/414) | np.delete (currently returns null) | @simonbuehler | | +| [#415](https://github.com/SciSharp/NumSharp/issues/415) | Boolean indexing and np.where | @joshmyersdean | | +| [#439](https://github.com/SciSharp/NumSharp/issues/439) | np.where (re-requested) | @minhduc66532 | missing feature/s | +| [#441](https://github.com/SciSharp/NumSharp/issues/441) | np.linalg.norm | @minhduc66532 | missing feature/s | +| [#445](https://github.com/SciSharp/NumSharp/issues/445) | np.dot with preallocated output array | @bigdimboom | missing feature/s | +| [#449](https://github.com/SciSharp/NumSharp/issues/449) | np.isclose / np.allclose (currently dead code) | @koliyo | missing feature/s | +| [#450](https://github.com/SciSharp/NumSharp/issues/450) | np.diag | @syemhusa | missing feature/s | +| [#454](https://github.com/SciSharp/NumSharp/issues/454) | np.linalg.lstsq (currently returns null) | @yangjiandendi | | +| [#464](https://github.com/SciSharp/NumSharp/issues/464) | np.random.triangular | @ppsdatta | | +| [#473](https://github.com/SciSharp/NumSharp/issues/473) | Bitwise shift and OR operators on NDArray | @MichielMans | | +| [#480](https://github.com/SciSharp/NumSharp/issues/480) | np.unravel_index | @iainross | | +| [#485](https://github.com/SciSharp/NumSharp/issues/485) | np.linalg.norm (re-requested) | @williamlzw | | +| [#486](https://github.com/SciSharp/NumSharp/issues/486) | Slice assignment (e.g. preds[:,:,0] = ...) | @burungiu | | +| [#497](https://github.com/SciSharp/NumSharp/issues/497) | np.linalg.pinv | @gsgou | | +| [#515](https://github.com/SciSharp/NumSharp/issues/515) | np.tile (re-requested) | @MaxOmlor | | + +### By Area + +**Linear Algebra:** +- [#105](https://github.com/SciSharp/NumSharp/issues/105) - np.vdot +- [#106](https://github.com/SciSharp/NumSharp/issues/106) - np.inner +- [#108](https://github.com/SciSharp/NumSharp/issues/108) - np.tensordot +- [#239](https://github.com/SciSharp/NumSharp/issues/239) - np.linalg.norm (full implementation) +- [#441](https://github.com/SciSharp/NumSharp/issues/441) - np.linalg.norm +- [#445](https://github.com/SciSharp/NumSharp/issues/445) - np.dot with preallocated output array +- [#454](https://github.com/SciSharp/NumSharp/issues/454) - np.linalg.lstsq (currently returns null) +- [#485](https://github.com/SciSharp/NumSharp/issues/485) - np.linalg.norm (re-requested) +- [#497](https://github.com/SciSharp/NumSharp/issues/497) - np.linalg.pinv + +**Array Creation / Manipulation:** +- [#75](https://github.com/SciSharp/NumSharp/issues/75) - np.asarray +- [#202](https://github.com/SciSharp/NumSharp/issues/202) - np.pad +- [#220](https://github.com/SciSharp/NumSharp/issues/220) - np.flip +- [#221](https://github.com/SciSharp/NumSharp/issues/221) - np.rot90 +- [#374](https://github.com/SciSharp/NumSharp/issues/374) - np.append +- [#378](https://github.com/SciSharp/NumSharp/issues/378) - np.frombuffer +- [#397](https://github.com/SciSharp/NumSharp/issues/397) - np.tile +- [#413](https://github.com/SciSharp/NumSharp/issues/413) - np.split +- [#414](https://github.com/SciSharp/NumSharp/issues/414) - np.delete (currently returns null) +- [#450](https://github.com/SciSharp/NumSharp/issues/450) - np.diag +- [#480](https://github.com/SciSharp/NumSharp/issues/480) - np.unravel_index +- [#486](https://github.com/SciSharp/NumSharp/issues/486) - Slice assignment (e.g. preds[:,:,0] = ...) +- [#515](https://github.com/SciSharp/NumSharp/issues/515) - np.tile (re-requested) + +**Logic / Selection / Indexing:** +- [#78](https://github.com/SciSharp/NumSharp/issues/78) - np.where +- [#210](https://github.com/SciSharp/NumSharp/issues/210) - np.all (with axis support) +- [#360](https://github.com/SciSharp/NumSharp/issues/360) - np.any (with axis support) +- [#365](https://github.com/SciSharp/NumSharp/issues/365) - np.nonzero +- [#415](https://github.com/SciSharp/NumSharp/issues/415) - Boolean indexing and np.where +- [#439](https://github.com/SciSharp/NumSharp/issues/439) - np.where (re-requested) +- [#445](https://github.com/SciSharp/NumSharp/issues/445) - np.dot with preallocated output array +- [#449](https://github.com/SciSharp/NumSharp/issues/449) - np.isclose / np.allclose (currently dead code) + +**Statistics:** +- [#373](https://github.com/SciSharp/NumSharp/issues/373) - np.median + +**Random:** +- [#298](https://github.com/SciSharp/NumSharp/issues/298) - np.random.choice (weighted sampling) +- [#464](https://github.com/SciSharp/NumSharp/issues/464) - np.random.triangular + +**Other:** +- [#114](https://github.com/SciSharp/NumSharp/issues/114) - np.fft.fft +- [#473](https://github.com/SciSharp/NumSharp/issues/473) - Bitwise shift and OR operators on NDArray + +--- + +## Feature Requests + +Non-np.* enhancements: architecture, tooling, new capabilities, ecosystem. + +| Issue | Title | Author | Labels | +|-------|-------|--------|--------| +| [#95](https://github.com/SciSharp/NumSharp/issues/95) | Extend the guidelines | @dotChris90 | | +| [#111](https://github.com/SciSharp/NumSharp/issues/111) | NumSharp GPU acceleration | @Oceania2018 | enhancement, help wanted, further discuss | +| [#116](https://github.com/SciSharp/NumSharp/issues/116) | Intel Math Kernel Library (MKL) | @Oceania2018 | enhancement, help wanted | +| [#129](https://github.com/SciSharp/NumSharp/issues/129) | Doc : Better specification for all classes (What class has what task) | @dotChris90 | further discuss | +| [#190](https://github.com/SciSharp/NumSharp/issues/190) | Compressed Sparse Format | @Oceania2018 | enhancement | +| [#211](https://github.com/SciSharp/NumSharp/issues/211) | implement scipy interpolate? | @xinqipony | enhancement | +| [#284](https://github.com/SciSharp/NumSharp/issues/284) | [Discussion] Ground Rules and Library Structure/Architecture | @Nucs | further discuss | +| [#326](https://github.com/SciSharp/NumSharp/issues/326) | Lazy loading | @aidevnn | further discuss | +| [#340](https://github.com/SciSharp/NumSharp/issues/340) | Memory Limitations | @Nucs | enhancement | +| [#341](https://github.com/SciSharp/NumSharp/issues/341) | NDArray string problem | @lokinfey | missing feature/s | +| [#343](https://github.com/SciSharp/NumSharp/issues/343) | Built-in System.Drawing.Image and Bitmap methods | @Nucs | enhancement | +| [#349](https://github.com/SciSharp/NumSharp/issues/349) | Scipy.Signal | @natank1 | missing feature/s | +| [#351](https://github.com/SciSharp/NumSharp/issues/351) | Proper way to iterate using IEnumerable | @Nucs | enhancement | +| [#361](https://github.com/SciSharp/NumSharp/issues/361) | Mixing indices and slices in NDArray[...] | @Oceania2018 | enhancement | +| [#363](https://github.com/SciSharp/NumSharp/issues/363) | Add `NDIterator` overload with support for specific axis. | @Nucs | missing feature/s | +| [#372](https://github.com/SciSharp/NumSharp/issues/372) | Clustering Example | @turowicz | help wanted, missing feature/s | +| [#375](https://github.com/SciSharp/NumSharp/issues/375) | Slice assignment? | @solarflarefx | missing feature/s | +| [#435](https://github.com/SciSharp/NumSharp/issues/435) | Complex number support? | @cgranade | | +| [#479](https://github.com/SciSharp/NumSharp/issues/479) | Lacking/Outdated Documentation | @Tianmaru | | +| [#500](https://github.com/SciSharp/NumSharp/issues/500) | The fact that such an excellent project has many unimplemented APIs and is no longer being maintained is regrettable. | @HCareLou | | +| [#523](https://github.com/SciSharp/NumSharp/issues/523) | Multi-threading supported? | @sawyermade | | + +--- + +## Performance Issues + +Reports of NumSharp being significantly slower than expected. + +| Issue | Title | Author | Summary | +|-------|-------|--------|---------| +| [#421](https://github.com/SciSharp/NumSharp/issues/421) | Performance | @mishun | Overall performance far slower than expected vs naive C# | +| [#427](https://github.com/SciSharp/NumSharp/issues/427) | Performance on np.matmul | @Banyc | np.matmul 100x slower than NumPy (3-4s vs 0.03s) | +| [#451](https://github.com/SciSharp/NumSharp/issues/451) | np.argmax is slow | @feiyuhuahuo | np.argmax very slow on large arrays (~500ms) | +| [#509](https://github.com/SciSharp/NumSharp/issues/509) | Extremely poor performance on sum reduce | @lucdem | sum(axis:0) 150x slower than NumPy | + +--- + +## Questions / How-To / Off-Topic + +Usage questions, conversion help, off-topic requests. + +| Issue | Title | Author | +|-------|-------|--------| +| [#70](https://github.com/SciSharp/NumSharp/issues/70) | Let more people know about NumSharp | @Oceania2018 | +| [#238](https://github.com/SciSharp/NumSharp/issues/238) | How to mimic Python's nice column and row access (i.e matrix[:, 2])? | @henon | +| [#383](https://github.com/SciSharp/NumSharp/issues/383) | is there any way to convert NumSharp.NDArray to Numpy.NDarray? | @lelelemonade | +| [#384](https://github.com/SciSharp/NumSharp/issues/384) | Save NDArray as png image | @solarflarefx | +| [#386](https://github.com/SciSharp/NumSharp/issues/386) | how to read .csv file with Numsharp? | @guang7400613 | +| [#390](https://github.com/SciSharp/NumSharp/issues/390) | How to create an NDArray from pointer and NPTypeCode? | @LarryThermo | +| [#401](https://github.com/SciSharp/NumSharp/issues/401) | How to convert NDArray to list | @Sullivanecidi | +| [#406](https://github.com/SciSharp/NumSharp/issues/406) | C# --> convert image to NDarray | @R06921096Yen | +| [#411](https://github.com/SciSharp/NumSharp/issues/411) | PyObject to NDArray | @aaronavi | +| [#416](https://github.com/SciSharp/NumSharp/issues/416) | how to make NumSharp.NDArray from Numpy.NDarray? | @djagatiya | +| [#424](https://github.com/SciSharp/NumSharp/issues/424) | The type or namespace name 'NumSharp' could not be found (are you missing a using directive or an assembly reference?) [Assembly-CSharp]csharp(CS0246) | @rcffc | +| [#438](https://github.com/SciSharp/NumSharp/issues/438) | How to get the inverse of a 2D matrix? | @Mingrui-Yu | +| [#446](https://github.com/SciSharp/NumSharp/issues/446) | Unable to use np.dot due to "Specified method unsupported" error | @moonlitlyra | +| [#462](https://github.com/SciSharp/NumSharp/issues/462) | How to use the repo to convert some Python code? | @zydjohnHotmail | +| [#465](https://github.com/SciSharp/NumSharp/issues/465) | how could I transform between NumSharp.NDArray with Tensorflow.Numpy.NDArray? | @cross-hello | +| [#472](https://github.com/SciSharp/NumSharp/issues/472) | How to calculate the rank of a matrix with NumSharp? | @drtujugkhjk | +| [#481](https://github.com/SciSharp/NumSharp/issues/481) | Normal disttribution in NumSharp | @rthota90 | +| [#482](https://github.com/SciSharp/NumSharp/issues/482) | 大佬,能否用C#还原 java 的一个求解器 optaplanner | @JavaScript-zt | +| [#483](https://github.com/SciSharp/NumSharp/issues/483) | How to convert List to NDArray | @williamlzw | +| [#494](https://github.com/SciSharp/NumSharp/issues/494) | Hello, has SciSharp/NumSharp stopped development and maintenance? | @sdyby2006 | +| [#495](https://github.com/SciSharp/NumSharp/issues/495) | What is the exposed method for percentiles & median using np? | @vikassingh281 | +| [#496](https://github.com/SciSharp/NumSharp/issues/496) | Can NumSharp fit polynomial surface equations? | @liyu3519 | +| [#498](https://github.com/SciSharp/NumSharp/issues/498) | Is there an example on how to use it with IronPython? | @william19941994 | +| [#510](https://github.com/SciSharp/NumSharp/issues/510) | How to save a nested dictionary with `save_npz` | @rqx110 | +| [#511](https://github.com/SciSharp/NumSharp/issues/511) | Does Numsharp work in Unity with the IL2CPP backend? | @jacob-jacob-jacob | +| [#512](https://github.com/SciSharp/NumSharp/issues/512) | how to change to Microsoft.ML.OnnxRuntime.Tensors.DenseTensor? | @BingGitCn | +| [#516](https://github.com/SciSharp/NumSharp/issues/516) | Very helpful work. Keep at it | @duxuan11 | diff --git a/docs/issues/issue-0070-let-more-people-know-about-numsharp.md b/docs/issues/issue-0070-let-more-people-know-about-numsharp.md new file mode 100644 index 000000000..b4cab28f8 --- /dev/null +++ b/docs/issues/issue-0070-let-more-people-know-about-numsharp.md @@ -0,0 +1,123 @@ +# #70: Let more people know about NumSharp + +- **URL:** https://github.com/SciSharp/NumSharp/issues/70 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2018-11-08T03:07:08Z +- **Updated:** 2019-08-06T13:03:10Z +- **Labels:** help wanted + +## Description + +I've written an article [here](https://medium.com/@haiping008/numsharp-numerical-net-7c6ab6edfe27) which introduce NumSharp. + +@dotChris90 Can you start with the [docs](https://numsharp.readthedocs.io). + +## Comments + +### Comment 1 by @dotChris90 (2018-11-08T06:24:14Z) + +Would start maybe at weekends. Next 2 days have little bit more work to do. :) + +### Comment 2 by @dotChris90 (2018-11-08T10:19:18Z) + +@Oceania2018 just one little thing. Sorry I mention it. + +Shouldn't it be: + +var a = np.arange(9).Reshape(3,3)? + +So with capital R instead of small r? +The output of arange is a NDArray object. So we call its Reshape method. + +What we are doing is method chaining. So we do not use the np.reshape method (if exist didn't look ^^') but the NDArray Reshape method. + +### Comment 3 by @dotChris90 (2018-11-08T10:46:38Z) + +@Oceania2018 and question : shall we start doc with python sphinx? Or maybe take docfx? https://dotnet.github.io/docfx/ + +Just idk if sphinx can parse c# for api documentations by doc comments. + +### Comment 4 by @Oceania2018 (2018-11-08T11:34:21Z) + +We should use both of them, docfx is for generating md files, Sphinx is for organizing them, and make docs readable on readthedocs.io. + + +I prefer arange().reshape(), keep same as numpy as much as possible. + +### Comment 5 by @dotChris90 (2018-11-17T18:19:34Z) + +@Oceania2018 at the moment I experiment with docfx - it does not look as bad as i though. Especially the API documentation and the style looks promising. Moreover docfx is the one which is used from Microsoft I though. The hosting we could do on Github Pages. + +Just feel annoyed at sphinx python doc, that it highlight my C# code but it thinks it is python lol so the code looks not good. And was investigating for C# docs at moment. + +How you think of docfx? i tried at https://dotchris90.github.io/NumSharp/ I will not merge it to Scisharp/NumSharp - so dont worry but I will see how well this can look like. + +### Comment 6 by @Oceania2018 (2018-11-17T18:30:20Z) + +Does docfx generate markdown file? I think docfx is used to generate developer's API reference, because it is generated from souce code directly. Sphinx is often used to make tutorial document, like quick start. Sphinx support markdown file, it's easy to use. + +### Comment 7 by @dotChris90 (2018-11-17T18:58:24Z) + +Yes they are now able to do it. I was also surprised. Seems they developed quite well. + +You can write so called articles which is equivalent sphinx tutorials. Using markdown. Very easy seems. So tutorials and api and swagger rest api and more seems (ok rest API is not important for us....). + +I can maybe experiment with it and upload to my repo. If we think it looks good we can go with it. If not we take sphinx. + +### Comment 8 by @dotChris90 (2018-11-18T17:01:07Z) + +@Oceania2018 an example for our api +https://dotchris90.github.io/NumSharp/api/NumSharp.NDArray-1.html + +And example for article : +https://dotchris90.github.io/NumSharp/articles/NDArray.Creation.html + +@Oceania2018 if u not too angry with me. I would like to go with docfx. They plan to support multiple languages besides c# in future and using 100% md for articles. Plus the generated docs folder can be hosted 100% on github for free. + +The only thing that I am not satisfied is... The style. I look now for better template... + + + + +### Comment 9 by @Oceania2018 (2018-11-19T02:29:48Z) + +@dotChris90 Docfx looks awesome. keep going. + +### Comment 10 by @dotChris90 (2018-11-19T02:48:29Z) + +@Oceania2018 thanks. Before thinking about pull requests. Is NumSharps github page active? If not, could you activate it? Not sure if I can since u the founder / constructor of SciSharp organizations. :) + +### Comment 11 by @Oceania2018 (2018-11-19T03:22:12Z) + +@dotChris90 It's active. https://scisharp.github.io/NumSharp/ + +### Comment 12 by @dotChris90 (2018-11-19T03:29:29Z) + +@Oceania2018 thanks a lot. Then today maybe write some more user tutorials. ;) +And push. + +### Comment 13 by @Oceania2018 (2018-11-19T03:32:43Z) + +Sounds great. NumSharp got 80+ stars now. Seems like people really need it. + +### Comment 14 by @dotChris90 (2018-11-19T03:42:26Z) + +Totally. Before c# was not the language of numeric. Since this year Microsoft push this AI and ML topics so hard... But Microsoft didn't give us a numerical stack. + +But people deserve and need a numerical stack like NumSharp. :) + +### Comment 15 by @dotChris90 (2018-11-19T11:52:18Z) + +Ok it worked. Just merged the docs which I already made. Nothing new. But will write some tutorials for user and also a small Readme how to use docfx best. + +It's time we dotnet developers go deeper to science, machine learning and numerics. + +### Comment 16 by @fdncred (2018-11-19T13:54:26Z) + +docfx is looking good. + +### Comment 17 by @Nucs (2019-08-06T13:03:10Z) + +@henon @Oceania2018, Should we implement a documentation page like discussed here? +https://dotnet.github.io/docfx/ diff --git a/docs/issues/issue-0075-implement-numpy.asarray.md b/docs/issues/issue-0075-implement-numpy.asarray.md new file mode 100644 index 000000000..9e880accd --- /dev/null +++ b/docs/issues/issue-0075-implement-numpy.asarray.md @@ -0,0 +1,19 @@ +# #75: Implement numpy.asarray + +- **URL:** https://github.com/SciSharp/NumSharp/issues/75 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2018-11-09T05:00:28Z +- **Updated:** 2018-11-14T22:04:06Z +- **Labels:** enhancement + +## Description + +Convert the input to an array. +https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.asarray.html + +## Comments + +### Comment 1 by @Obrain2016 (2018-11-14T22:04:06Z) + +adopt diff --git a/docs/issues/issue-0078-implement-numpy.where.md b/docs/issues/issue-0078-implement-numpy.where.md new file mode 100644 index 000000000..23c8e2a3f --- /dev/null +++ b/docs/issues/issue-0078-implement-numpy.where.md @@ -0,0 +1,19 @@ +# #78: Implement numpy.where + +- **URL:** https://github.com/SciSharp/NumSharp/issues/78 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2018-11-09T05:07:46Z +- **Updated:** 2019-12-05T02:00:14Z +- **Labels:** enhancement + +## Description + +Return elements, either from x or y, depending on condition. +https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.where.html + +## Comments + +### Comment 1 by @rakshithm (2019-12-05T02:00:14Z) + +When can we expect this to be implemented? diff --git a/docs/issues/issue-0095-extend-the-guidelines.md b/docs/issues/issue-0095-extend-the-guidelines.md new file mode 100644 index 000000000..3c348f144 --- /dev/null +++ b/docs/issues/issue-0095-extend-the-guidelines.md @@ -0,0 +1,59 @@ +# #95: Extend the guidelines + +- **URL:** https://github.com/SciSharp/NumSharp/issues/95 +- **State:** OPEN +- **Author:** @dotChris90 +- **Created:** 2018-11-10T18:36:50Z +- **Updated:** 2019-08-06T13:05:05Z +- **Assignees:** @Nucs + +## Description + +@Oceania2018 @fdncred I started our Wiki (was not a lot but at least some lines ....) + +On https://github.com/Oceania2018/NumSharp/wiki/Design-guidelines I started a page with guidelines. +Because I found our discussions and investigations last weeks very interesting and want to write them down. Especially casting, performance, arange instead of ARange, ... such things. + +If you do not like some parts, or want to add something, feel free to change. ;) + +## Comments + +### Comment 1 by @Oceania2018 (2018-11-12T23:03:19Z) + +Looks great. Good job! + +### Comment 2 by @Oceania2018 (2018-11-14T23:28:39Z) + +@dotChris90 @fdncred I've wrote an article [here](https://medium.com/@haiping008/numsharp-works-with-matplotlib-213f1753480). + +### Comment 3 by @fdncred (2018-11-15T00:17:59Z) + +Awesome! Good job. Maybe you should put your c# demo app in the repo as an example. + +### Comment 4 by @dotChris90 (2018-11-15T03:44:36Z) + +Amazing. I really was impressed when saw it. 🤩 + +### Comment 5 by @dotChris90 (2018-11-15T03:58:07Z) + +By the way. Technically background question. + +You used a matplotlib adapter and in background also using matplotlib. Amazing. + +I was wondering how much work it would be to make a own matplotlib but html based. + +In my mind hat sth like +- tiny web sever in background +- plotly.js as drawing lib +- chromely project for rendering + +### Comment 6 by @Oceania2018 (2018-11-15T04:00:32Z) + +Our hard work is worth it. NumSharp is providing enough methods to support high level library. +@dotChris90 I don't want to implement a ploting library for now. Let's focus on NumSharp and Pandas.NET. + +### Comment 7 by @dotChris90 (2018-11-15T04:08:50Z) + +Sure and agree. + +OH seems the f# guys already have sth like this with their xplot project so maybe even later no reason for us to implement diff --git a/docs/issues/issue-0105-implement-numpy.vdot.md b/docs/issues/issue-0105-implement-numpy.vdot.md new file mode 100644 index 000000000..9737253e6 --- /dev/null +++ b/docs/issues/issue-0105-implement-numpy.vdot.md @@ -0,0 +1,22 @@ +# #105: Implement numpy.vdot + +- **URL:** https://github.com/SciSharp/NumSharp/issues/105 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2018-11-15T03:48:18Z +- **Updated:** 2018-11-15T16:48:44Z +- **Labels:** enhancement +- **Assignees:** @tatadyj + +## Description + +Return the dot product of two vectors. +https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.vdot.html#numpy.vdot + +`ndarray.vdot` should be put in `LinearAlgebra` folder. + +## Comments + +### Comment 1 by @tatadyj (2018-11-15T16:45:36Z) + +give a try vdot diff --git a/docs/issues/issue-0106-implement-numpy.inner.md b/docs/issues/issue-0106-implement-numpy.inner.md new file mode 100644 index 000000000..c33e59e0f --- /dev/null +++ b/docs/issues/issue-0106-implement-numpy.inner.md @@ -0,0 +1,16 @@ +# #106: Implement numpy.inner + +- **URL:** https://github.com/SciSharp/NumSharp/issues/106 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2018-11-15T03:49:36Z +- **Updated:** 2018-12-28T07:03:26Z +- **Labels:** help wanted +- **Milestone:** v0.7 + +## Description + +Inner product of two arrays. +https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.inner.html#numpy.inner + +`ndarray.inner` should be put in `LinearAlgebra` folder. diff --git a/docs/issues/issue-0108-implement-numpy.tensordot.md b/docs/issues/issue-0108-implement-numpy.tensordot.md new file mode 100644 index 000000000..fedf72821 --- /dev/null +++ b/docs/issues/issue-0108-implement-numpy.tensordot.md @@ -0,0 +1,16 @@ +# #108: Implement numpy.tensordot + +- **URL:** https://github.com/SciSharp/NumSharp/issues/108 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2018-11-15T03:51:17Z +- **Updated:** 2018-12-28T07:03:44Z +- **Labels:** help wanted +- **Milestone:** v0.7 + +## Description + +Compute tensor dot product along specified axes for arrays >= 1-D. +https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.tensordot.html#numpy.tensordot + +`ndarray.tensordot` should be put in `LinearAlgebra` folder. diff --git a/docs/issues/issue-0111-numsharp-gpu-acceleration.md b/docs/issues/issue-0111-numsharp-gpu-acceleration.md new file mode 100644 index 000000000..74c59aa86 --- /dev/null +++ b/docs/issues/issue-0111-numsharp-gpu-acceleration.md @@ -0,0 +1,26 @@ +# #111: NumSharp GPU acceleration + +- **URL:** https://github.com/SciSharp/NumSharp/issues/111 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2018-11-16T18:08:54Z +- **Updated:** 2022-02-26T16:16:26Z +- **Labels:** enhancement, help wanted, further discuss + +## Description + +We might use [Campy](https://github.com/kaby76/Campy) to accelerate NumSharp. + +## Comments + +### Comment 1 by @fdncred (2018-11-16T18:25:03Z) + +@Oceania2018, I was going to suggest the same thing. We don't want to be writing cuda .cu files. It's not easy. However, my first suggestion would be to parallelize the code where possible. i.e. on `IEnumerables` we could use `.AsParallel()`, on for loops we could use `Parallel.For` and `Parallel.Foreach`. BenchmarkDotNet will be helpful here. + +### Comment 2 by @ZhiZe-ZG (2022-02-26T15:11:37Z) + +This function is very important in some occasions where accelerated computing is required but neural networks (and torch) are not suitable. + +### Comment 3 by @Oceania2018 (2022-02-26T16:16:26Z) + +We switched to [Tensorflow.Numpy](https://github.com/SciSharp/TensorFlow.NET/tree/master/src/TensorFlowNET.Core/NumPy). diff --git a/docs/issues/issue-0114-implement-numpy.fft.fft.md b/docs/issues/issue-0114-implement-numpy.fft.fft.md new file mode 100644 index 000000000..ccd05fdec --- /dev/null +++ b/docs/issues/issue-0114-implement-numpy.fft.fft.md @@ -0,0 +1,13 @@ +# #114: Implement numpy.fft.fft + +- **URL:** https://github.com/SciSharp/NumSharp/issues/114 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2018-11-17T04:35:25Z +- **Updated:** 2018-11-17T04:35:47Z +- **Labels:** enhancement + +## Description + +Compute the one-dimensional discrete Fourier Transform. +https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.fft.fft.html#numpy.fft.fft diff --git a/docs/issues/issue-0116-intel-math-kernel-library-mkl.md b/docs/issues/issue-0116-intel-math-kernel-library-mkl.md new file mode 100644 index 000000000..cd84ae8de --- /dev/null +++ b/docs/issues/issue-0116-intel-math-kernel-library-mkl.md @@ -0,0 +1,321 @@ +# #116: Intel Math Kernel Library (MKL) + +- **URL:** https://github.com/SciSharp/NumSharp/issues/116 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2018-11-17T15:12:25Z +- **Updated:** 2019-05-02T16:53:31Z +- **Labels:** enhancement, help wanted +- **Assignees:** @dotChris90 +- **Milestone:** v0.7 + +## Description + +Performance-sensitive algorithms can be swapped with alternative implementations by the concept of providers like Intel MKL. +https://software.intel.com/en-us/performance-libraries + +## Comments + +### Comment 1 by @dotChris90 (2018-12-12T20:01:13Z) + +Intels MKL library implement the LAPACK interfaces - so it would be logical to think about a layer with different LAPACK providers - in other words - a strategy pattern. Let users decide which LAPACK provider they want to use. + +It will be not much work since LAPACK usual means --> all interfaces are the same. So the PInvoke class would be the same - just the native lib is different. + +As fdncred already said. :) + +### Comment 2 by @Oceania2018 (2018-12-12T20:01:52Z) + +Sounds great. + +### Comment 3 by @fdncred (2018-12-12T20:05:26Z) + +This is exactly what I was saying in #145 when I mentioned and linked to Armadillo. + +### Comment 4 by @dotChris90 (2018-12-13T07:27:39Z) + +@fdncred sorry yes i know but I want to mention it here at this place ;) + +### Comment 5 by @Oceania2018 (2018-12-13T18:04:00Z) + +@dotChris90 Please add MKL as a new provider when you have a chance. + +### Comment 6 by @dotChris90 (2018-12-13T18:29:30Z) + +yes. but after seeing Intel website I think we go strategy that mkl provider expects the lib installed. on their site they said u have to registry etc..... So.... I don't wanna quarrel with them. in case we simple copy their DLLs and publish them with NumSharp.... think Intel will be not happy. + +so installation must be done manually by user. + +At moment not sure what is most comfortable way to change providers. + +My suggestion is that static numpy class has a static property lapack provider which is an enum. + +The static lapack class will check this enum and will call the specific static lapack provider classes. + +The user can change provider global. I think most comfortable. + +### Comment 7 by @fdncred (2018-12-13T19:04:47Z) + +@dotChris90, I'll research this a bit because other people, namely microsoft, point to dlls such as this https://docs.microsoft.com/en-us/cognitive-toolkit/setup-mkl-on-windows. + +Now that I look closer, this one ls mklml and is opensource by intel. Perhaps that is different. + +### Comment 8 by @fdncred (2018-12-13T19:12:12Z) + +Check this out. Intel allows one to redistribute MKL. It's in the license. +https://software.intel.com/en-us/mkl/license-faq + +And here is the full license. +https://software.intel.com/en-us/license/intel-simplified-software-license + + +### Comment 9 by @fdncred (2018-12-13T19:20:21Z) + +Here's a few Github projects that wrap MKL in C#. May be easier just to use something that already exists. +https://github.com/DNRY/CSIntelPerfLibs +https://github.com/Rafka86/SharpMKL +https://github.com/Proxem/BlasNet + + +### Comment 10 by @dotChris90 (2018-12-13T19:40:01Z) + +@fdncred much thanks for investigation. This makes the situation much better. 👍 + +### Comment 11 by @dotChris90 (2018-12-14T07:26:10Z) + +Hm - ok it will not be so easy as I though. + +The problem is that MKL implement just a subset of LAPACK and it implement LAPACKE - not LAPACK. They are little bit different from interfaces. + +The LAPACK we have at moment (Standard) has about 1500 Functions - MKL has 450. But is much bigger .... NetLib is about 8MB and their MKL 150MB -.- + +Sorry I say - this issue will be open little bit longer because Intel just implement what they want .... + +### Comment 12 by @dotChris90 (2018-12-14T07:36:25Z) + +But do not worry - the Provider strategy I still want to follow. ;) + +### Comment 13 by @dotChris90 (2019-01-05T08:36:59Z) + +ok some news from my side. + +MKL is installable on Linux via https://gist.github.com/pachamaltese/afc4faef2f191b533556f261a46b3aa8 +and on Windows via PIP with command"pip install mkl". + +With this Libs can check what functions **are really exported**. + +@Oceania2018 @fdncred just one question to you 2. What you think about "put providers in separate packages?" Like "NumSharp.MKLProvider" and so on. The benefit would be --> do unit Tests and maybe automatic benchmark Tests just when the provider project made some changes and not NumSharp. +Appveyor would be capable of this. + +@Oceania2018 this would be maybe also a solution for Tensorflow.NET --> package the Libs as Provider in new Nuget Package. + +### Comment 14 by @Oceania2018 (2019-01-05T11:21:41Z) + +Seems like if we separate NumSharp.MKLProvider, we need to separate an Interface project too, then we will have 3 separate NuGet packages, right? + +### Comment 15 by @fdncred (2019-01-05T13:36:58Z) + +I like the idea of separate packages because it gives users a choice. + +### Comment 16 by @dotChris90 (2019-01-05T14:40:46Z) + +@Oceania2018 I think so. at least easiest solution. also agree with @fdncred. people can free choose. maybe we even can pack mkl in nuget package like python do. since pip offers nuget also could do. and we could offer nuget package with tensorflow C Api. + + I think it could be best solution because people can use mkl if they like or not. or take standard lapack. + +by the way on Friday I checked the CI of the standard open source lapack. with appveyor was able to build the DLLs and shared objects myself. + +If appveyor offers mac OS images we also could support Mac OS. + + +### Comment 17 by @Oceania2018 (2019-01-05T14:53:49Z) + +@dotChris90 you can try to add a new project. + +### Comment 18 by @dotChris90 (2019-01-05T20:34:53Z) + +Ok before do the stuff just want to clear with you 2 (@Oceania2018 @fdncred ) the plan. + +Our NumSharp repo would consist of + +- NumSharp.Core => our main project with no provider just abstraction (like an interface ILAPACKProvider) +- NumSharp.NetLibLAPACK => it provides the default LAPACK implementation from https://github.com/Reference-LAPACK/lapack (we could fork it and build C# wrapper classes - already was able to set it up on appveyor and generate some *.dlls +- NumSharp.IntelMKL => it provides the MKL from Intel with LAPACK (just can deliver the binaries - no source code) +- Ironpython and Powershell projects but they are not relevant for our providers + +Beside This I suggest one more thing +- folder "NumSharp.Meta" with a nuget spec file +- the NumSharp.Meta folder with its nuget spec file shall be used to generate an metapackage called simple "NumSharp" (this NumSharp package should add multiple references to users project like NumSharp.Core + NumSharp.NetLibLAPACK - in future we can add more but for now this 2 references are enough) +- why we should do it? --> users just need to do "dotnet add package NumSharp" (so 1 instead of 2 add package commands) and can start! but! important! they can remove the reference to NetLibLAPACK by themselves and add the IntelMKL - and everybody is happy. ;) + +@Oceania2018 for Tensorflow.NET I also suggest this solution with meta-package - so we deliver Tensorflow C API as package for users. + +### Comment 19 by @dotChris90 (2019-01-05T20:45:28Z) + +But also the annoying news .... Since we take a provider strategy we must take an OOP strategy pattern and ... they do not work with static methods. This just means our providers must be implemented as non-static classes with constructor - or other said - they must implement an interface. + +This is no bad thing just brings more code. we need our static methods to adapt the native libraries and we need the non static methods to implement interface. + +### Comment 20 by @Oceania2018 (2019-01-05T22:08:17Z) + +@dotChris90 Where is the interface project? We should have another Interface project dll. + +### Comment 21 by @dotChris90 (2019-01-07T19:46:35Z) + +hm I am not sure at moment. + +We could make a separate project and package or keep it in NumSharp.Core. +At moment think about pros and cons. + +### Comment 22 by @Oceania2018 (2019-01-07T19:52:20Z) + +OK, do more research. + +### Comment 23 by @buybackoff (2019-01-07T19:59:49Z) + +Have you looked at Math.Net MKL provider? Or at Math.Net integration at all? As far as I remember Math.Net has its own native wrapper over which they interop, but it should be trivial to amend if all parties agree. I'm following this project/stack for a while and happy if Python stack is repeated 1-to-1 in .NET, but having several options that basically do the same stuff seems sad. Especially given the size of MKL dependency. + +Cc @cdrnet + +### Comment 24 by @Oceania2018 (2019-01-07T20:57:15Z) + +@dotChris90 Do you know how much size of the MKL dll? As my experience of TensorFlow.NET, tensorflow.dll is about 65M, luckly the NuGet compress it to about 16M. So I pack the dll directly into NuGet, It's acceptable. + +### Comment 25 by @pkingwsd (2019-01-08T02:46:16Z) + +as below from MathNet + + +Licensing Restrictions +Be aware that unlike the core of Math.NET Numerics including the native wrapper, which are both open source under the terms of the MIT/X11 license, the Intel MKL binaries themselves are closed source and non-free. + +The Math.NET Numerics project does own an Intel MKL license (for Windows, no longer for Linux) and thus does have the right to distribute it along Math.NET Numerics. You can therefore use the Math.NET Numerics MKL native provider for free for your own use. However, it does not give you any right to redistribute it again yourself to customers of your own product. If you need to redistribute, buy a license from Intel. If unsure, contact the Intel sales team to clarify. + +@Oceania2018 +MathNet.Numerics.MKL.Win-x64 nuget package, the size about 136 MB, mathnet.numerics.mkl.linux-x64 about 134m + + +### Comment 26 by @Oceania2018 (2019-01-08T02:50:27Z) + +@pkingwsd It means we can't pack MKL into NumSharp's package? @dotChris90 so we just have to place the download link for user? + +### Comment 27 by @pkingwsd (2019-01-08T02:57:28Z) + +@Oceania2018 i thank so that ,just make NumSharp's MKL for windows package. + +### Comment 28 by @dotChris90 (2019-01-08T07:00:00Z) + +@buybackoff thanks for mention it. Actually I was thinking about this **BUT** (there is always a but ... ) I am really unsure about Intel Licensing etc. Not sure if we can use their Math.NET Libs .... thats the only reason. +To be honest - we plan that at the end is just one meta-data package called **NumSharp** and this contains references to **NumSharp.Core** and **NetLibLAPACK** package. + +For NetLibLAPACK Standard - I already tried out their build system with my personal account https://github.com/dotChris90/lapack. I am sure if we mention them in our Readme its ok for them. + +MKL is tricky. For standard user who just want to learn and get things done I prefer the NetLibLAPACK Lib. which will be reference when using "dotnet add package NumSharp" in future. But .... when we cleared this discussion about MKL .... we want to give user the chance to remove this NetLibLAPACK reference from their project and use MKL intel. This will reduce the space in memory and hard disk space. + +### Comment 29 by @dotChris90 (2019-01-08T07:04:37Z) + +@Oceania2018 @pkingwsd damn ... this license stuff get more complex .... but yes he is right. **If you do not mind - I will write an email to intel sales team to clear things.** + +I am little confused at moment since the Linux MKL is a simple debian based package and can be downloaded any time. So I though there is no restriction for windows also. But I am not sure so far. Because I can not find official windows repo for MKL ..... + +### Comment 30 by @dotChris90 (2019-01-08T15:00:49Z) + +@Oceania2018 @pkingwsd what about this section https://software.intel.com/en-us/mkl/license-faq ? +In the FAQ stand Yes, redistribution is allowed per the terms of the ISSL. and also no payment etc. + +### Comment 31 by @Oceania2018 (2019-01-08T15:18:30Z) + +Great, so we don't need to worry about the license problem per the official statement. + +### Comment 32 by @dotChris90 (2019-01-08T15:41:53Z) + +still wrote to them. I want a statement of them.... BTW fdncred also mentioned it before lol the faq but as I said I wait for their response. + +### Comment 33 by @fdncred (2019-01-08T16:04:00Z) + +yeah! someone reads my comments. LOL! ;) + +### Comment 34 by @dotChris90 (2019-01-08T16:09:44Z) + +@fdncred definitely. I am just highly confused about the mkl libraries out there. + +is anaconda mkl the same like normal mkl and why there is no package for windows... the apt get installer also just download the file and put at specific locations... + +drives me crazy.... + +### Comment 35 by @fdncred (2019-01-08T16:47:58Z) + +@dotChris90 it appears to me that anaconda's MKL is just the regular MKL. It also looks to me like there is a windows package. See the links below. +https://anaconda.org/anaconda/mkl +https://docs.anaconda.com/accelerate/mkl-overview/ +https://repo.anaconda.com/pkgs/main/win-64/ +For my two cents, I think Proxem's BlasNet is the best wrapper for MKL. It looks pretty mature. +https://github.com/Proxem/BlasNet + +### Comment 36 by @dotChris90 (2019-01-08T21:13:48Z) + +@fdncred Thanks for the update. ;) + +From the URL I agree with you. 👍 +I already downloaded the package via pip and checked it but the package brings multiple MKL dlls. In anaconda location you will find binaries with mkl_corem mkl_xyz, …. about 6 or 7 files. But I though the mkl thing is one single DLL …. but anaconda shows multiple .... this is extrem strange behaviour... + +I agree Proxems BlasNet looks fine but it does not solve our main problem. Proxem just brings Wrapper code in C# - please do not misunderstand me but I am not very impressed by this …. because there are a lot of C# Code-Generators who do the same job - e.g. ClangSharp, T4, SWIG and if I check the Mono team I am 100% sure I can find more generators. xD Such automatic wrapper always simple take a C header file and generate Code …. I really do not see any benefit to use it BlasNet. I saw the Code and as far as I can see the main methods we can use could be also generated automaticly by ClangSharp (or T4) so not sure why add package for something we can auto generate xD. + +Our (at least what makes me feel uncomfortable) problem is : Proxem does not bring a native MKL Lib package. I have to install it manually. In Linux its easy - "sudo apt-get install" but on windows it is impossible to do this. This makes CI Testing impossible. How do we want to do unit testing or automatic benchmark testing on Appveyor? Unfortunately -.- we can not do this - except we package this MKL DLL into a nuget package. damn windows has no apt get.... + + +### Comment 37 by @fdncred (2019-01-08T22:08:23Z) + +Probably not impossible for CI. Have you looked at using chocolatey to install MKL? It looks like one of microsoft's packages includes it https://chocolatey.org/packages/microsoft-r-open. It may not be perfect but it may work. + +I've had a lot of experience with ClangSharp and NClang as well as other generators to take a C/C++ library to C# and I'm quite sure that they are not easy. I've found with one for months trying to get the library ported and it's very time consuming. You can get close pretty easily but you can't get complete easily. It'll take work for sure. + +### Comment 38 by @dotChris90 (2019-01-09T05:16:00Z) + +damn choco package management didn't think on it yes. can try. thanks. + +If u say it is hard maybe I was too easy thinking. the only complex thing we could have with code generation is.... lapack 100 % uses pointer. so generator can not know what's array and what pass by reference. + +hm...yes could be trickier. but as long as we just using some few functions from lapack.... writing own is easier. but yes maybe later need some more professional things. + +### Comment 39 by @fdncred (2019-01-09T13:56:05Z) + +Yes, IntPtr from C/C++ can be tricky and requires hand massaging for every call, from my experience. Marshalling arrays isn't too complex if you know the count of the array and the size of the array. Ref, In, Out params can be tricky, especially if you have an [In, Out] parameter in C/C++, where you pass in out value in a parameter and you return a different value out of the same parameter. If you get stuck on something reach out to me and I may be able to help. + +### Comment 40 by @Oceania2018 (2019-01-09T14:11:46Z) + +@fdncred can you help with +https://github.com/SciSharp/TensorFlow.NET/issues/86? + +### Comment 41 by @fdncred (2019-01-09T14:17:14Z) + +@Oceania2018, yes, i'll look at it. I'm not promising i can fix it but I'll try. ;) + +### Comment 42 by @Oceania2018 (2019-01-09T14:22:30Z) + +Anyway I’d appreciate. Try to make it work. Enjoy tensorflow.net. + +### Comment 43 by @dotChris90 (2019-01-10T06:06:45Z) + +for traceability. Intel just offer you help if u have customer service.. yeah every company need money I know.... + +But post in forum and got answer direct from Intel. + +https://software.intel.com/en-us/comment/1932183#comment-1932183 + +as we can read there. don't worry take the mkl lib. + +answers was (2019 Jan 10th, 7:09 Berlin time) + +please refer here : license FAQ + +you won't have a problem redistributing it. + +### Comment 44 by @mzhukova (2019-05-02T16:53:31Z) + +Hi folks, +I saw on the Intel forum that you were asking about distributing MKL via nuget. +It is available now in nuget channel as well. I was just wondering, if this may be useful to you: https://www.nuget.org/packages?q=intelmkl + +Best regards, +Maria diff --git a/docs/issues/issue-0129-doc-better-specification-for-all-classes-what-class-has-what-task.md b/docs/issues/issue-0129-doc-better-specification-for-all-classes-what-class-has-what-task.md new file mode 100644 index 000000000..04af6c0ee --- /dev/null +++ b/docs/issues/issue-0129-doc-better-specification-for-all-classes-what-class-has-what-task.md @@ -0,0 +1,92 @@ +# #129: Doc : Better specification for all classes (What class has what task) + +- **URL:** https://github.com/SciSharp/NumSharp/issues/129 +- **State:** OPEN +- **Author:** @dotChris90 +- **Created:** 2018-11-23T17:16:31Z +- **Updated:** 2018-12-14T12:25:49Z +- **Labels:** further discuss + +## Description + +Since now NumSharp offers a system for non generic, generic, dynamic and static we are in a good position for further dialog / discuss about classes tasks. +Moreover this issue can be used for the developer documentation. + +Let us call it "tidy up" and think about what we really need and most important need to think about the SW design. + +At the moment I see the following classes and their tasks + +- Storage (which could be a child of interface class IStorage) + - store the Data somehow (abstract said - concrete store in 1D System.Array) + - Get and Set Data in this Store (all at once or by indexing) + - convert Data to specific type +- Shape ( best practice would be child of IShape) + - store the dimensions of the array + - compute the index of a 1D array by multiple indexes if array is a multidimensional array + - compute the multiple indexes of multidimensional array from the index of 1D array +- Core.NDArray (non generic ) + - 1 NDArray has 1 Storage & 1 Shape + - delivers many methods for manipulate, processes, … 1 or N different Core.NDArrays. + - since its non generic elements are ValueTypes (must be cast to double etc.) +- Generic.NDArray + - children of Core.NDArray which offers possibility to direct indexing since elements are of generic parameter type + +@Oceania2018 @fdncred you 2 ;) please correct me if I wrote something wrong. + +@Oceania2018 Sorry for my stupid questions always - is there a reason that a Storage shall have a shape? Its really just a design question. I just think for Testability Classes shall be as independent as possible from each others. If they do not need a relationship then they should not have. From my point of view since NDArray has a shape, Storage does not need a shape. Storage shall not care about its Shape and just contain the elements. Just our best friend NDArray shall use Storage and Shape to bring elements in correct form. + +;) anyway guys have a good week and nice weekend. + + + + + +## Comments + +### Comment 1 by @Oceania2018 (2018-11-23T18:39:50Z) + +Excellent concept describing. For shape in storage, my idea is storage should be responsible for persisting and seeking data from any dimension and position, NDArray’s main responsibility is calculating, not seek data form storage. We are open for this topic. And NumPy is responsible for exposing interfaces to external invoking. + +### Comment 2 by @dotChris90 (2018-11-23T18:51:25Z) + +hm good point. + +from testability point of view storage should not have a shape. +from logic I am not sure. + +We keep issue open and I look again the code. what fits better. + +It's really just design question. user should not care this because they are not official Apis. + +### Comment 3 by @dotChris90 (2018-11-28T04:12:16Z) + +I come up with decision. + +I agree with you. the storage should be responsible for every data access and converting stuff. + +But then we must be careful. Ndarray shall have no own shape. if necessary it shall call methods from its storage object. dtype same. +ndarray must return the dtype of storage at e. g. it's property dtype. + +### Comment 4 by @dotChris90 (2018-12-14T12:24:42Z) + +@Oceania2018 @fdncred + +I added an interface for storage and one for shape. This is a future planning so the storage get easier and more clear to use. I come up with this because I noticed the storages methods sometimes not 100% well ..... most was my mistake -.- + +e.g. GetData() return the internal 1D array but! if dtype is corresponding to T its the reference array and if not its a copy .... the data types are correct but the underlying pointers are different ..... and since you all so interested in performance, memory, etc. such things can not be. + + +The interfaces are suggestions for next generation Storage lol https://github.com/SciSharp/NumSharp/blob/master/src/NumSharp.Core/Interfaces/IStorage.cs + +As you see I made up the following convensions : + +- new property TensorOrder (maybe should call it TensorLayout) +- Allocate will be used to choose Shape, data type and! TensorOrder +- GetData always return the reference of internal storage ALWAYS! +- if dtype is not equal to T in GetData< T > the internal storage is converted automaticly! +- New method CloneData which always will give you a copy of the internal storage and not storage itself +- so all in all GetData is copy by reference, CloneData is copy by value +- GetColumWiseStorage & GetRowWiseStorage methods return the instance itself but with column wise or row wise layout +- This layout stuff is extrem critical for LAPACK - including MKL! since they just accept rowwise and we at moment use columnwise ;) + + diff --git a/docs/issues/issue-0190-compressed-sparse-format.md b/docs/issues/issue-0190-compressed-sparse-format.md new file mode 100644 index 000000000..c3da3bf0f --- /dev/null +++ b/docs/issues/issue-0190-compressed-sparse-format.md @@ -0,0 +1,35 @@ +# #190: Compressed Sparse Format + +- **URL:** https://github.com/SciSharp/NumSharp/issues/190 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2019-01-09T21:45:32Z +- **Updated:** 2019-04-16T16:00:05Z +- **Labels:** enhancement +- **Assignees:** @dotChris90 +- **Milestone:** v0.7 + +## Description + +We should implement the compressed ndarray, it is used widely in scikit-learn. The csr_matrix is included in SciPy not NumPy, but I think we should move it into NumSharp. @dotChris90 What do you think of it? + +[Compressed Sparse Column Format (CSC)](https://www.scipy-lectures.org/advanced/scipy_sparse/csc_matrix.html) +[Compressed Sparse Row Format (CSR)](https://www.scipy-lectures.org/advanced/scipy_sparse/csr_matrix.html) + +https://www.scipy-lectures.org/advanced/scipy_sparse/storage_schemes.html + +## Comments + +### Comment 1 by @Deep-Blue-2013 (2019-01-09T21:47:51Z) + +CSC format saved memory a lot. Looking forward to see this feature. + +### Comment 2 by @sebhofer (2019-04-16T13:38:38Z) + +Would be great to have an sparse matrix implementation with good performance! Math.Net has some support for sparse matrices, but performance is not great. Also, would be useful to have support for different storage formats (as you already hinted at above). + +As an additional reference: [Here](https://github.com/wo80/mathnet-extensions) is an implementation improving on the performance of Math.Net. + +### Comment 3 by @Oceania2018 (2019-04-16T16:00:05Z) + +@sebhofer Thanks for the info, it's important to us. diff --git a/docs/issues/issue-0202-implement-np.pad.md b/docs/issues/issue-0202-implement-np.pad.md new file mode 100644 index 000000000..6c83840a6 --- /dev/null +++ b/docs/issues/issue-0202-implement-np.pad.md @@ -0,0 +1,25 @@ +# #202: implement np.pad + +- **URL:** https://github.com/SciSharp/NumSharp/issues/202 +- **State:** OPEN +- **Author:** @skywalkerisnull +- **Created:** 2019-03-03T04:25:59Z +- **Updated:** 2019-03-10T01:06:49Z +- **Labels:** enhancement +- **Assignees:** @KevinMa0207 +- **Milestone:** v0.7.5 + +## Description + +Add a buffer or padding around a numpy array: +https://docs.scipy.org/doc/numpy/reference/generated/numpy.pad.html + +## Comments + +### Comment 1 by @Oceania2018 (2019-03-03T15:13:32Z) + +```python +a = [1, 2, 3, 4, 5] +np.pad(a, (2,3), 'constant', constant_values=(4, 6)) + +``` diff --git a/docs/issues/issue-0210-add-numpy.all.md b/docs/issues/issue-0210-add-numpy.all.md new file mode 100644 index 000000000..332f2de1d --- /dev/null +++ b/docs/issues/issue-0210-add-numpy.all.md @@ -0,0 +1,20 @@ +# #210: Add numpy.all + +- **URL:** https://github.com/SciSharp/NumSharp/issues/210 +- **State:** OPEN +- **Author:** @Esther2013 +- **Created:** 2019-03-10T04:32:04Z +- **Updated:** 2019-04-05T07:56:30Z +- **Assignees:** @Oceania2018, @KevinMa0207 + +## Description + +Test whether all array elements along a given axis evaluate to True. +`np.all([[True,False],[True,True]])` +https://docs.scipy.org/doc/numpy/reference/generated/numpy.all.html + +## Comments + +### Comment 1 by @henon (2019-04-05T07:56:30Z) + +I just implemented this, but without axis support at the moment. The overload with axis is still to be done diff --git a/docs/issues/issue-0211-implement-scipy-interpolate.md b/docs/issues/issue-0211-implement-scipy-interpolate.md new file mode 100644 index 000000000..8dac97056 --- /dev/null +++ b/docs/issues/issue-0211-implement-scipy-interpolate.md @@ -0,0 +1,32 @@ +# #211: implement scipy interpolate? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/211 +- **State:** OPEN +- **Author:** @xinqipony +- **Created:** 2019-03-12T02:27:15Z +- **Updated:** 2019-03-26T05:14:11Z +- **Labels:** enhancement +- **Assignees:** @Oceania2018 + +## Description + +this is an amazing project and any plan to implement scipy interpolate? + +## Comments + +### Comment 1 by @Oceania2018 (2019-03-12T02:50:02Z) + +Will do `Interpolate a 1-D function.` and `2-D` + +### Comment 2 by @Oceania2018 (2019-03-26T04:37:13Z) + +@xinqipony Is https://docs.scipy.org/doc/numpy/reference/generated/numpy.interp.html you want? + +### Comment 3 by @xinqipony (2019-03-26T05:09:21Z) + +yes, this is what I used: +interpolate.interp1d(x, y, kind='cubic', axis=0), + +in the meantime, I use MathNet now, but its implementation is not so good and not easy to use, + +look forward to your new updates! diff --git a/docs/issues/issue-0220-numpy.flip.md b/docs/issues/issue-0220-numpy.flip.md new file mode 100644 index 000000000..e44c18e90 --- /dev/null +++ b/docs/issues/issue-0220-numpy.flip.md @@ -0,0 +1,20 @@ +# #220: numpy.flip + +- **URL:** https://github.com/SciSharp/NumSharp/issues/220 +- **State:** OPEN +- **Author:** @pkingwsd +- **Created:** 2019-03-21T03:29:13Z +- **Updated:** 2019-06-15T04:49:17Z +- **Labels:** enhancement +- **Assignees:** @yanglr + +## Description + +numpy.flip + +## Comments + +### Comment 1 by @Oceania2018 (2019-03-21T03:31:07Z) + +https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html +Please also past the python link, thanks. diff --git a/docs/issues/issue-0221-numpy.rot90.md b/docs/issues/issue-0221-numpy.rot90.md new file mode 100644 index 000000000..d22eb66c7 --- /dev/null +++ b/docs/issues/issue-0221-numpy.rot90.md @@ -0,0 +1,22 @@ +# #221: numpy.rot90 + +- **URL:** https://github.com/SciSharp/NumSharp/issues/221 +- **State:** OPEN +- **Author:** @pkingwsd +- **Created:** 2019-03-21T03:30:00Z +- **Updated:** 2021-01-25T22:55:22Z +- **Assignees:** @emrahsungu + +## Description + +numpy.rot90 + +## Comments + +### Comment 1 by @Oceania2018 (2019-03-21T03:31:46Z) + +https://docs.scipy.org/doc/numpy/reference/generated/numpy.rot90.html + +### Comment 2 by @solarflarefx (2021-01-25T22:53:39Z) + +@Oceania2018 @emrahsungu Does this exist in the current version of NumSharp? diff --git a/docs/issues/issue-0238-how-to-mimic-pythons-nice-column-and-row-access-i.e-matrix-2.md b/docs/issues/issue-0238-how-to-mimic-pythons-nice-column-and-row-access-i.e-matrix-2.md new file mode 100644 index 000000000..f15caa6b6 --- /dev/null +++ b/docs/issues/issue-0238-how-to-mimic-pythons-nice-column-and-row-access-i.e-matrix-2.md @@ -0,0 +1,401 @@ +# #238: How to mimic Python's nice column and row access (i.e matrix[:, 2])? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/238 +- **State:** OPEN +- **Author:** @henon +- **Created:** 2019-04-05T08:08:30Z +- **Updated:** 2020-02-08T11:40:24Z +- **Labels:** help wanted +- **Assignees:** @henon +- **Milestone:** v0.9 Multiple Backends + +## Description + +Hey all, + +How would you say to do access to a column `matrix[:, i]` or access to a row `matrix[i, :]` in NumSharp? + + +## Comments + +### Comment 1 by @henon (2019-04-05T11:21:07Z) + +I need to be able to get a (n-1)-dimensional slice of a n-dimensional matrix, i.e. a 1d vector out of a 2d matrix. +First step would be to add appropriate accessors to IStorage right? +I am not yet sure how they should look like, please comment if you have ideas. + +### Comment 2 by @henon (2019-04-05T11:35:01Z) + +This is not as elegantly possible as in Python, but we can do something like this, given that matrix is an NDArray: + +* matrix[Range.All, 1] ... return the first column, Python: matrix[:, 1] +* matrix[new Range(0, 2), 1] ... return the first two entries of the first column, Python: matrix[:2, 1] +* matrix[new Range(1,3), new Range(1,3)] ... return a 2x2 submatrix of matrix starting at 1,1, Python: matrix[1:3, 1:3] + + +### Comment 3 by @Oceania2018 (2019-04-05T11:35:18Z) + +Check `indexing` to help. +![image](https://user-images.githubusercontent.com/1705364/55624849-c97ce200-576c-11e9-8b03-5b4cac0766b0.png) + +We can override in `Slice` +![image](https://user-images.githubusercontent.com/1705364/55624914-f03b1880-576c-11e9-9456-2b4e97c17077.png) + +Or we can pass string `":1"`. + +### Comment 4 by @henon (2019-04-05T11:40:45Z) + +Yeah, the string idea is great for porting code from Python. I am sure it will be popular. + +### Comment 5 by @Oceania2018 (2019-04-05T11:43:02Z) + +Seem's like another contributor already implemented the string index. I'll found out later. @PppBr is that true? + +### Comment 6 by @henon (2019-04-05T11:45:05Z) + +when you slice a matrix in numpy you essentially get a view of the original data. modifying it will modify the original matrix. Do we have a view concept or implementation already? + +### Comment 7 by @Oceania2018 (2019-04-05T11:50:46Z) + +Not yet, we havn't have the `view` class. I've an idea how to implement the new `view`. + +`view` inherit from `ndarray`, but keep a `filter` of data index. +and `view` will implement a `IEnumerate` interface. +when interating the `view`, it will return all the element in the `filter`. +and view don't need copy the memory. change data will reflect in the original memory. + +@henon What do you think of it? + +or we need a new `NDStorage`? + +### Comment 8 by @henon (2019-04-05T12:14:49Z) + +@Oceania2018: I think the projection from the view to the original data could be handled entirely by the storage. + +This is complicated, I had to think about it for a time to wrap my head around it. If I am correct, what you need is a function that projects a view index onto an index on the original data (which could be another view that does further projection - think about that). + +I am not yet sure how that function looks like if you i.e. for instance get a 1D slice out of a 17-dimensional matrix but once we have that generic formula we can easily project view_index to underlying data_index and view_index+1, view_index+2, ... etc. to enumerate it. + + + + + + + +### Comment 9 by @Oceania2018 (2019-04-05T12:17:40Z) + +Actually, we have the formula to convert n-d index to 1d index and wise-verse. +chech the function `Shape.GetDimIndexOutShape` +So don't worry about the dimension. the storage is persistent data in 1d array. + +I still think the `view` should inherit from `ndarray` with a `filters` in `view` instance, not in `ndstorage` level. Let's keep this talk open. + +### Comment 10 by @henon (2019-04-05T12:47:10Z) + +Nice we have the projection and the inverse projection already, so the difficult part is actually already solved. The rest is just software design. + +One thing I missed, that the view also needs to enumerate on the underlying data (or view), is a stride. If you take a row out of a matrix the stride is 1, but if you take a column, the stride is the width of the matrix. + +ok, you may be right about inheriting from ndarray. I have only limited knowledge about the design at this point, so I trust your judgement. + +### Comment 11 by @Oceania2018 (2019-04-05T13:01:13Z) + +I will write the first version of view, then we can discuss further. + +### Comment 12 by @henon (2019-04-05T13:25:41Z) + +Don't forget to make the design recursive, so that you can have a `view` of a `view` of a `view` of a 'storage'. + +### Comment 13 by @Oceania2018 (2019-04-05T14:00:16Z) + +Sure + +### Comment 14 by @henon (2019-04-08T09:51:45Z) + +Update: Python's slice notation for accessing slices of NDArray has been implemented. However, the implementation of a view that serves that slice of the original data still needs to be done. + +Here is a very simple test case that demonstrates the problem to be solved: +```[TestMethod] + public void GetAColumnOf2dMatrix() + { + var x = np.arange(9).reshape(3, 3); + var v = x[":, 1"]; + Assert.IsTrue(Enumerable.SequenceEqual( v.Data(), new double[]{ 1, 4, 7 })); + v[1] = 99; + Assert.AreEqual(99, x[1,1]); + } +``` + +### Comment 15 by @Oceania2018 (2019-04-27T16:46:05Z) + +Check the `Slice3x2x2` UnitTest. + +### Comment 16 by @Rikki-Tavi (2020-02-07T03:35:42Z) + + +I have been wrestling with the same (or similar) issue in the context of trying to generate randomized mini-batches using NumSharp. +The python code I am trying to emulate looks like this: + +def random_mini_batches(X, Y, mini_batch_size = 64): + """ + Creates a list of random minibatches from (X, Y) + + Arguments: + X -- input data, of shape (input size, number of examples) + Y -- true "label" vector of shape (1, number of examples) + mini_batch_size - size of the mini-batches, integer + + Returns: + mini_batches -- list of synchronous (mini_batch_X, mini_batch_Y) + """ + + m = X.shape[1] # number of training examples + mini_batches = [] + + # Step 1: Shuffle (X, Y) + permutation = list(np.random.permutation(m)) + + shuffled_X = X[:, permutation] + shuffled_Y = Y[:, permutation].reshape((Y.shape[0],m)) + + # Step 2: Partition (shuffled_X, shuffled_Y). Minus the end case. + num_complete_minibatches = math.floor(m/mini_batch_size) # number of mini batches of size mini_batch_size in your partitionning + for k in range(0, num_complete_minibatches): + mini_batch_X = shuffled_X[:, k * mini_batch_size : k * mini_batch_size + mini_batch_size] + mini_batch_Y = shuffled_Y[:, k * mini_batch_size : k * mini_batch_size + mini_batch_size] + mini_batch = (mini_batch_X, mini_batch_Y) + mini_batches.append(mini_batch) + + # Handling the end case (last mini-batch < mini_batch_size) + if m % mini_batch_size != 0: + mini_batch_X = shuffled_X[:, num_complete_minibatches * mini_batch_size : m] + mini_batch_Y = shuffled_Y[:, num_complete_minibatches * mini_batch_size : m] + mini_batch = (mini_batch_X, mini_batch_Y) + mini_batches.append(mini_batch) + + return mini_batches + +The part I have trouble emulating with NumSharp is: + + shuffled_X = X[:, permutation] + shuffled_Y = Y[:, permutation].reshape((Y.shape[0],m)) + +The closest I have been able to get is: + + var shuffled_X = new NDArray(np.float32, X.shape); + for (int i = 0; i < X.shape[0]; i++) + { + shuffled_X[i] = X[i][permutation]; + } + + var shuffled_Y = new NDArray(np.float32, Y.shape); + for (int i = 0; i < Y.shape[0]; i++) + { + shuffled_Y[i] = Y[i][permutation]; + } + shuffled_Y = shuffled_Y.reshape((Y.shape[0], m)); + +Which works, but is much, much slower (also true when I implement the alternative in Python). + +The problem with NumSharp is that I cannot put the indexer ":, permutation" in quotes. I've tried converting 'permutation' to its string-expanded list equivalent (so that I can have a totally string-based indexer that NumSharp likes) but I don't seem to be able to arrive at a string-serialized encoding of 'permutation' inside the resulting indexer that doesn't make NumSharp crash. + +Am I overlooking an obvious solution here? + +### Comment 17 by @Oceania2018 (2020-02-07T04:20:06Z) + +@BillyDJr Try +```csharp +shuffled_X = X[Slice.All, new Slice(permutation)]; +``` + +### Comment 18 by @Rikki-Tavi (2020-02-07T05:50:40Z) + +Trying that yielded: +`NumSharp.IncorrectShapeException: 'This method does not work with this shape or was not already implemented.'` + + +### Comment 19 by @henon (2020-02-07T07:50:54Z) + +> @BillyDJr Try +> +> ```cs +> shuffled_X = X[Slice.All, new Slice(permutation)]; +> ``` + +No, he needs index access to get shuffled data. It should be + +```cs +shuffled_X = X[Slice.All, new NDArray(permutation)]; +``` + +### Comment 20 by @Rikki-Tavi (2020-02-07T21:11:11Z) + +I simplified my Python target test case, showing two ways to generate the shuffled Xs and Ys. +m = 4 +X = np.random.rand(3,m) +Y = np.random.rand(1,m) +permutation = list(np.random.permutation(m)) +print ("perm: " + str(permutation)) +print ("X: " + str(X)) +print ("Y: " + str(Y)) +shuffled_X1 = X[:, permutation] +shuffled_X2 = X.copy() +for i in range(0, X.shape[0]): + shuffled_X2[i] = X[i][permutation] + +print("shuffled_X1: " + str(shuffled_X1)) +print("shuffled_X2: " + str(shuffled_X2)) + +shuffled_Y1 = Y[:, permutation].reshape((Y.shape[0],m)) +shuffled_Y2 = Y.copy() +for i in range(0, Y.shape[0]): + shuffled_Y2[i] = Y[i][permutation] +shuffled_Y2 = shuffled_Y2.reshape((Y.shape[0], m)) + +print("shuffled_Y1 " + str(shuffled_Y1)) +print("shuffled_Y2 " + str(shuffled_Y2)) + +And the results: +perm: [2, 1, 3, 0] +X: [[0.14038694 0.19810149 0.80074457 0.96826158] + [0.31342418 0.69232262 0.87638915 0.89460666] + [0.08504421 0.03905478 0.16983042 0.8781425 ]] +Y: [[0.09834683 0.42110763 0.95788953 0.53316528]] +shuffled_X1: [[0.80074457 0.19810149 0.96826158 0.14038694] + [0.87638915 0.69232262 0.89460666 0.31342418] + [0.16983042 0.03905478 0.8781425 0.08504421]] +shuffled_X2: [[0.80074457 0.19810149 0.96826158 0.14038694] + [0.87638915 0.69232262 0.89460666 0.31342418] + [0.16983042 0.03905478 0.8781425 0.08504421]] +shuffled_Y1 [[0.95788953 0.42110763 0.53316528 0.09834683]] +shuffled_Y2 [[0.95788953 0.42110763 0.53316528 0.09834683]] + +Here is the C# code (just showing the Xs for the sake of brevity). Note the shuffled_X2 works (as I reported before, but it runs too slowly for large datasets). As you can see, I try various stabs at arriving at shuffled_X1, all failing: + + var m = 4; + var X = np.random.rand(3, m); + var permutation = np.random.permutation(m); + + print($"perm: {permutation}"); + print($"X: {X}"); + + // In Python: shuffled_X1 = X[:, permutation] + try + { + var shuffled_X1 = X[Slice.All, permutation]; + print($"shuffled_X1: {shuffled_X1}"); + } + catch (Exception ex) + { + print($"Param as: raw permutation - error: {ex.Message}"); + } + + try + { + var shuffled_X1 = X[Slice.All, new Slice(permutation)]; + print($"shuffled_X1: {shuffled_X1}"); + } + catch (Exception ex) + { + print($"Param as: new Slice(permutation) - error: {ex.Message}"); + } + + try + { + var shuffled_X1 = X[Slice.All, new NDArray(permutation.ToArray())]; + print($"shuffled_X1: {shuffled_X1}"); + } + catch (Exception ex) + { + print($"Param as: new NDArray(permutation.ToArray()) - error: {ex.Message}"); + } + + try + { + var shuffled_X1 = X[Slice.All, new NDArray(permutation.CloneData())]; + print($"shuffled_X1: {shuffled_X1}"); + } + catch (Exception ex) + { + print($"Param as: new NDArray(permutation.CloneData()) - error: {ex.Message}"); + } + + try + { + var shuffled_X1 = X[Slice.All, new NDArray(Binding.list(permutation.ToArray()))]; + print($"shuffled_X1: {shuffled_X1}"); + } + catch (Exception ex) + { + print($"Param as: new NDArray(Binding.list(permutation.ToArray())) - error: {ex.Message}"); + } + + var shuffled_X2 = new NDArray(np.float32, X.shape); + for (int i=0; i()) - error: shape mismatch: objects cannot be broadcast to a single shape +Param as: new NDArray(permutation.CloneData()) - error: shape mismatch: objects cannot be broadcast to a single shape +Param as: new NDArray(Binding.list(permutation.ToArray())) - error: shape mismatch: objects cannot be broadcast to a single shape +shuffled_X2: [[0.03434474, 0.8834703, 0.5201029, 0.55567724], +[0.6067407, 0.7806033, 0.3361534, 0.7949469], +[0.60182047, 0.11560028, 0.83518714, 0.2805164]] + +Are there any other variations I should try? + + + + +### Comment 21 by @Rikki-Tavi (2020-02-08T04:45:32Z) + +FWIW....It was easy to get working with Numpy.NET: + + [TestMethod] + public void mytest() { + var m = 4; + var X = np.random.rand(3, m); + var permutation = np.random.permutation(m); + + print($"perm: {permutation}"); + print($"X: {X}"); + + var shuffled_X = X[":", permutation]; + print($"shuffled_X: {shuffled_X}"); + } + + private void print (string output) + { + System.Diagnostics.Debug.WriteLine(output); + } + +Resulted in: + +``` +perm: [3 0 1 2] +X: [[0.58033439 0.99341353 0.77685615 0.1893294 ] + [0.81824309 0.7315536 0.64120989 0.97876454] + [0.15659539 0.9045345 0.83839889 0.43254176]] +shuffled_X: [[0.1893294 0.58033439 0.99341353 0.77685615] + [0.97876454 0.81824309 0.7315536 0.64120989] + [0.43254176 0.15659539 0.9045345 0.83839889]] +``` +Now I'm off to investigate if there is an efficient way that I can use Nump.NET NDArrays for my mini-batch randomization in TensorFlow.NET. (I'm admittedly just hacking away here....please stop me, anyone, if you think there is a better way.) + + +### Comment 22 by @henon (2020-02-08T09:41:39Z) + +Obviously, this should also work in NumSharp, but there seems to be a bug somewhere. + +### Comment 23 by @Rikki-Tavi (2020-02-08T11:40:24Z) + +Ok. Thanks. I will keep my eye out for the resolution, once it appears, so that i might switch over. diff --git a/docs/issues/issue-0239-np.linalg.norm.md b/docs/issues/issue-0239-np.linalg.norm.md new file mode 100644 index 000000000..323c6fe8a --- /dev/null +++ b/docs/issues/issue-0239-np.linalg.norm.md @@ -0,0 +1,49 @@ +# #239: np.linalg.norm + +- **URL:** https://github.com/SciSharp/NumSharp/issues/239 +- **State:** OPEN +- **Author:** @henon +- **Created:** 2019-04-05T10:52:03Z +- **Updated:** 2020-10-09T10:41:13Z +- **Labels:** enhancement + +## Description + +I am going to port np.linalg.norm(...) + +## Comments + +### Comment 1 by @Oceania2018 (2019-04-05T15:35:08Z) + +It's not same as ? +![image](https://user-images.githubusercontent.com/1705364/55639218-77e54f00-578e-11e9-8320-89364ddc6805.png) + + +### Comment 2 by @henon (2019-04-05T16:36:31Z) + +ok, you are right. that implements the L2 norm I need. I'll add the shortcut np.linalg.norm(...) just for Python compatibility + +### Comment 3 by @henon (2019-04-05T16:44:54Z) + +no, after checking the code against the numpy docs, actually, the 2-norm is not the same what has been implemented in normalize(). There is no squaring of coefficients. Is that a bug? + +https://het.as.utexas.edu/HET/Software/Numpy/reference/generated/numpy.linalg.norm.html + +### Comment 4 by @Oceania2018 (2019-04-05T18:02:55Z) + +Yes, they're different. + +### Comment 5 by @8 (2020-10-09T07:23:27Z) + +Hi, + +I also noticed that the function is missing in `0.20.5`. +I think one could add a shortcut from `np.linalg.norm(a)` => `np.sqrt(a.dot(a))` instead, which should be equivalent. +Not sure about the overloads though. + +Does this make sense to you? + +Take care, +Martin + + diff --git a/docs/issues/issue-0284-discussion-ground-rules-and-library-structure-architecture.md b/docs/issues/issue-0284-discussion-ground-rules-and-library-structure-architecture.md new file mode 100644 index 000000000..e26ed2f56 --- /dev/null +++ b/docs/issues/issue-0284-discussion-ground-rules-and-library-structure-architecture.md @@ -0,0 +1,96 @@ +# #284: [Discussion] Ground Rules and Library Structure/Architecture + +- **URL:** https://github.com/SciSharp/NumSharp/issues/284 +- **State:** OPEN +- **Author:** @Nucs +- **Created:** 2019-06-14T15:33:31Z +- **Updated:** 2021-06-29T07:16:05Z +- **Labels:** further discuss + +## Description + +I couldn't but notice the inconsistencies around the library, There are functions that returns copies while some don't while some are not even complete. +The strong-typing of C# does surely makes it harder to get stuff done so `.tt` generators does a nice job helping with that. + +I would like to discuss the following: +1. What are the supported C# primitive types compared to numpy? +Looking at the implicit conversions of `NDArray` and other math operations such as `np.sum`, +I found that the type support is inconsistent. +note: Numpy supports both signed and unsigned types. [see this](https://www.numpy.org/devdocs/user/basics.types.html). + + ![image](https://user-images.githubusercontent.com/649919/59519128-ddad3000-8ecf-11e9-9091-552d86184504.png) + +2. Do we support `Complex`? +because aside of the basic math operations, it is not supported anywhere. + +3. To what rank/ndim the algorithms in C# support? +I saw some functions that support 2-3 and some up to 6. +[Numpy limitation is 32 dimensions.](https://github.com/numpy/numpy/issues/5744) + +4. NDArray mutable vs immutable + I think it should be up to the user to decide via a bool parameter. + * Arithmetic operators should always return a copy + * Following numpy's lead is the priority ofcouse. + +5. Seperation between `np`, `NDArray` and `backend engine` +There is a lot of mixup between whats computed where. +The reality is that they all redirect calls to each other. +In a perfect scenario: +we would refer all operations low-level operations to backend engine to perform the wanted task. +Having a fallback to each method that will compute in C#. +`NDArray` is only responsible for casting, initialization, storing data and serialization +And `np` is the high-level zone that uses backend. + +6. The incomplete methods should be logged somewhere, preferably in Issues or project here. +Heres just a few examples from my findings: + * `np.transpose` supports only to rank 2 and theres a `//todo` in the unit test that fails + * `np.flatten` wasn't implemented completely but was shipped on release + * `np.sum` doesn't support all data types. +We should start by creating issues for those problems in code and we'll get on from there + +7. Means of communication between the developing team via instant messaging? +Edit: Did not see the gitter tag on readme.md + +Those issues are important to address, simply because this library is and will be the backend of many high-level applications. +That's about it, Best regards. + +## Comments + +### Comment 1 by @henon (2019-06-14T17:00:28Z) + +Hi Nucs, +you are right, there are many inconsistencies due to the fact that people just added what they needed and left the rest undone. We should define stricter rules and postulate them in the readme. + +I think the most important guidance principle must be compatibility with the original NumPy. Only that way we can guarantee that porting Python scripts using numpy will work in NumSharp also. In this regard there is a lot of work to be done because until now it wasn't enforced very strongly. + +I agree that some kind of management of what we have and what we are missing would be nice. Creating issues is of course a good idea. I have another one: create as many test cases as possible which will reflect what already works and what not. I have recently ported a lot of tests from the Numpy documentation for NumSharp's sister project [Numpy.NET](https://github.com/SciSharp/Numpy.NET) which is a auto-generated wrapper around numpy. While doing that I also generated over 600 test cases which could be repurposed for testing NumSharp. In addition to that, we can generate further test cases that test both Numpy.NET and NumSharp against all the things you mentioned: +* number of supported ranks +* supported data types +* support for all backend engines +etc. + +A complete test suite is the ultimate todo list and helps keeping NumSharp in sync with numpy. + +PS: I could help generating more test cases from the Numpy.NET project. + +### Comment 2 by @henon (2019-06-14T17:25:44Z) + +ad 7: you can reach us on Gitter for quick questions, small talk, etc, but of course important stuff that should be accessible to all (like this) is of course best discussed in issues. I am sure @Oceania2018 will have answers for the other questions. + +### Comment 3 by @Oceania2018 (2019-06-16T00:02:33Z) + +ad 1: Ideally, support all primitive data type. +ad 2: Not support, but can add easily. +ad 3: In theory, no limitation. +ad 4: Following numpy's lead. +ad 5: Should do the calculation in `engine`, move current code into `engine` gradually. +ad 6: Agree. +ad 7: https://gitter.im/sci-sharp/community + +### Comment 4 by @Nucs (2019-08-06T12:47:33Z) + +All of these suggestions were implemented in the [new release](https://github.com/SciSharp/NumSharp/pull/336) of NumSharp 0.11.0-alpha2 and available on [nuget](https://www.nuget.org/packages/NumSharp/0.11.0-alpha2). + +### Comment 5 by @dcuccia (2021-06-29T00:51:14Z) + +ad 2: Would love to have Complex support. Just got to the end of a port and realized this was a non-starter. diff --git a/docs/issues/issue-0298-implement-numpy.random.choice.md b/docs/issues/issue-0298-implement-numpy.random.choice.md new file mode 100644 index 000000000..04a97bbf0 --- /dev/null +++ b/docs/issues/issue-0298-implement-numpy.random.choice.md @@ -0,0 +1,96 @@ +# #298: Implement numpy.random.choice + +- **URL:** https://github.com/SciSharp/NumSharp/issues/298 +- **State:** OPEN +- **Author:** @Plankton555 +- **Created:** 2019-06-27T09:42:50Z +- **Updated:** 2019-10-05T16:12:40Z +- **Labels:** enhancement +- **Assignees:** @Nucs + +## Description + +Generates a random sample from a given (possible weighted) 1-D array. + +NumPy docs: https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.choice.html + +## Comments + +### Comment 1 by @Plankton555 (2019-06-27T09:48:16Z) + +I needed this and implemented a subset of this functionality (random sampling based on weighted probabilities). I can clean up that code and upload here so that it maybe can work as a start for this functionality. + +I've never contributed to an open-source project before though, so I might need some help when it comes to what must be implemented, and where in the architecture it should be located, and so on. + +### Comment 2 by @henon (2019-06-27T10:15:05Z) + +One of us (maybe @Nucs ?) can provide the method stub for you to fill in the code. You can then look at that commit to learn which files needed to be touched to add a new function. + +### Comment 3 by @Plankton555 (2019-06-28T10:11:27Z) + +Please do! Let me know when that is done. + +### Comment 4 by @Nucs (2019-06-30T20:02:39Z) + +Sorry it took so long, it should be placed in `NumSharp.Core/Random/np.random.choice.cs` +```C# +/// +/// //todo +/// +/// If an ndarray, a random sample is generated from its elements. If an int, the random sample is generated as if a were np.arange(a) +/// +/// The probabilities associated with each entry in a. If not given the sample assumes a uniform distribution over all entries in a. +/// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.choice.html +public NDArray choice(NDArray arr, Shape shape, double[] probabilities = null) { + throw new NotImplementedException(); +} + +/// +/// //todo +/// +/// If an ndarray, a random sample is generated from its elements. If an int, the random sample is generated as if a were np.arange(a) +/// +/// The probabilities associated with each entry in a. If not given the sample assumes a uniform distribution over all entries in a. +/// https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.choice.html +public NDArray choice(int a, Shape shape, double[] probabilities = null) { + throw new NotImplementedException(); +} +``` +Feel free to contact us via [gitter](https://gitter.im/sci-sharp/community) if you get stuck or have a question + +### Comment 5 by @Plankton555 (2019-07-02T14:16:04Z) + +Working on this in https://github.com/Plankton555/NumSharp/tree/feature/np_random_choice + +### Comment 6 by @Plankton555 (2019-07-04T13:52:33Z) + +One of the examples in the numpy docs looks like +``` +>>> aa_milne_arr = ['pooh', 'rabbit', 'piglet', 'Christopher'] +>>> np.random.choice(aa_milne_arr, 5, p=[0.5, 0.1, 0.1, 0.3]) +array(['pooh', 'pooh', 'pooh', 'Christopher', 'piglet'], + dtype='|S11') +``` + +Since the numsharp method signature either takes an integer or an NDArray I tried implementing this string[] list with an NDArray. This gives an exception (which maybe should be reported as a bug or nonimplemented feature since numpy arrays can take strings). +``` +NDArray aa_milne_arr = new string[] { "pooh", "rabbit", "piglet", "Christopher" }; // throws System.NotImplementedException: implicit operator NDArray(Array array) +``` + +In this particular case I can solve it in some ways: +1. Let this error happen until the NDArray has support for strings. +2. Have another method signature which takes an array/enumerable of some sort. This could be reasonable since the numpy docs explicitly states that np.random.choice "Generates a random sample from a given 1-D array", which should be possible to represent as an enumerable? + +Any thoughts? + +### Comment 7 by @Oceania2018 (2019-07-04T13:55:59Z) + +@Nucs Do you have any plan on `String` support? + +### Comment 8 by @Plankton555 (2019-07-04T14:31:40Z) + +Pull request at https://github.com/SciSharp/NumSharp/pull/310 + +### Comment 9 by @Nucs (2019-10-05T16:12:40Z) + +This issue is still open because `np.random.choice` does not support multi-dimensions. diff --git a/docs/issues/issue-0315-tostring-should-truncate-its-output.md b/docs/issues/issue-0315-tostring-should-truncate-its-output.md new file mode 100644 index 000000000..c80c199dd --- /dev/null +++ b/docs/issues/issue-0315-tostring-should-truncate-its-output.md @@ -0,0 +1,40 @@ +# #315: ToString should truncate its output + +- **URL:** https://github.com/SciSharp/NumSharp/issues/315 +- **State:** OPEN +- **Author:** @thomasd3 +- **Created:** 2019-07-18T12:32:09Z +- **Updated:** 2019-07-18T13:56:23Z +- **Labels:** bug, enhancement +- **Assignees:** @Nucs + +## Description + +On very large arrays, NDArray.ToString() is never coming back (or maybe it would at some point in time). + +It would make sense to truncate the ToString() output. + +## Comments + +### Comment 1 by @Nucs (2019-07-18T12:49:04Z) + +I'll add a `DebuggerTypeProxy` that'll truncute beyond certain amount of characters. +After we finish the rework on the NumSharp's backend - It should be much faster. + +### Comment 2 by @henon (2019-07-18T13:30:42Z) + +I implemented the NDArray.ToString() but at the time I didn't know that Numpy has a way of not showing the middle elements of a huge array. if you create a 100x100 matrix it will show this on the console. the ToString method probably should do the same. See below. Also, current ToString() does not format the elements so that they are equally spaced with a monotype font. + +``` +>>> import numpy as np +>>> a=np.arange(10000).reshape(100,100) +>>> a +array([[ 0, 1, 2, ..., 97, 98, 99], + [ 100, 101, 102, ..., 197, 198, 199], + [ 200, 201, 202, ..., 297, 298, 299], + ..., + [9700, 9701, 9702, ..., 9797, 9798, 9799], + [9800, 9801, 9802, ..., 9897, 9898, 9899], + [9900, 9901, 9902, ..., 9997, 9998, 9999]]) +>>> +``` diff --git a/docs/issues/issue-0326-lazy-loading.md b/docs/issues/issue-0326-lazy-loading.md new file mode 100644 index 000000000..0f398865b --- /dev/null +++ b/docs/issues/issue-0326-lazy-loading.md @@ -0,0 +1,122 @@ +# #326: Lazy loading + +- **URL:** https://github.com/SciSharp/NumSharp/issues/326 +- **State:** OPEN +- **Author:** @aidevnn +- **Created:** 2019-08-01T13:30:31Z +- **Updated:** 2019-09-25T17:58:40Z +- **Labels:** further discuss +- **Assignees:** @Nucs + +## Description + +Hi +Your lib NumSharp was inspired me and i tried to write a other approach of NumPy more easy to use. +The big idea is the Lazy Loading for the front end and this is my repo. +https://github.com/aidevnn/DesertLand + +My lib isnt optimized for speed at this time, but i can improve it with ease by rewritting NDarray backend class and methods WITHOUT changing nothing from the NDview struct frontend which use the lazy loading. + +Actually your Backend is very very powerfull and maybe the frontend can be also improved with Lazy Loading. + +Best regards. + +## Comments + +### Comment 1 by @Oceania2018 (2019-08-01T13:46:01Z) + +@aidevnn Join us, build Deep Learning library based on current work. + +### Comment 2 by @aidevnn (2019-08-01T13:52:23Z) + +I started to fork your repo, it takes me some month to understand in deep your great work. +Best regards. + +### Comment 3 by @Nucs (2019-08-01T15:01:17Z) + +Hey @aidevnn, Thank you. +A. Are you aware we are rewriting our backend in a [separate branch](https://github.com/SciSharp/NumSharp/tree/unmanaged-bytes-storage)? +B. Can you elaborate more about what do you mean by a front-end lazy loading? +If I understand correctly, lazy-loading is something that numpy does not implement therefore if you wish to contribute a lazy-loading front-end when NumSharp is the backend - it'll have to be in a seperate project. + +### Comment 4 by @aidevnn (2019-08-01T19:23:20Z) + +Hello @Nucs + +A. Ok a devel branch is more collaborative than a fork + +B. Lazy Loading can separate the SharpNum library onto frontend with simple to write specification which will be used by the rest of SciSharp ML/AI framework and the pure SharpNum backend for fast and speed computation. The SciSharp ML/AI team can write their own unit test to integrate SharpNum progression. +Lazy Loading is a design pattern wich allow to build complex expressions and he acts sometime like -proxy-. It is also a possible abstraction for integrating multiple different backend but it isnt a good idea at this moment for the framework SciSharp. + +For example, the expression +NDproxy d = a+2*b+np.log(c); +NDarray e = d; +will only be evaluated during the implicit type conversion. +I am a self taugh and i discovered the lazy loading just now to reduce computation. I have some intuition about some other usage of the Lazy Loading, but i cannot explain it. + +### Comment 5 by @Oceania2018 (2019-08-01T19:35:35Z) + +@aidevnn Is `NDProxy` like a kind of Expression/ Func/ Delegate ? It can be executed after all operations have been decided ? + +### Comment 6 by @aidevnn (2019-08-01T19:56:43Z) + +@Oceania2018 Yes it is a struct that can be defined like this during internal computation of the backend +``` +NDproxy a = new NDproxy(()=>ndArray0) +``` + +Its contains only one field / property : +``` +Func fnc +``` +which can be called explicitly +``` +NDarray b = a.fnc() +``` +during internal computation of the backend +or implicitly during type conversion by the frontend. + +In my repo i used delegate + struct. +https://github.com/aidevnn/DesertLand/blob/master/NDarray/NDview.cs#L4 + +### Comment 7 by @Nucs (2019-08-01T20:04:54Z) + +@aidevnn +A. You can still use that branch from a fork +B. Thanks for laying it out for me, I am too a self taught. +Honestly - there is no fast way to stack lazy loading ops. Any basic implementation in C++ will surpass C#'s performance by far. +Your best luck is to put efforts in [Tensorflow.NET](https://github.com/SciSharp/TensorFlow.NET) since tensorflow holds a somewhat similar API to numpy and is capable of computation on a GPU and it provides a pretty impressive performance (both CPU and GPU). + +if you do insist on writing it on your own then you better get started with IL generation that will link the math ops together. +Mainly because every math-op method you delegate to the next math-op method is adding more overhead than you would think. + +`NumSharp` itself is not the fastest it can be. Any library that utilizes the performance of a low-level languages will be faster than our unmanaged algorithms. + +### Comment 8 by @aidevnn (2019-08-01T20:11:27Z) + +@Nucs you are right! Lazy Loading adds some stacks operations because its a kind of managing expression pattern. +Managing code have advantage for simplifying coding and maintenance, but with loss of speed and performance. Its a tradeoff. + +### Comment 9 by @aidevnn (2019-08-02T19:38:38Z) + +@Nucs The idea behind the lazy loading is to introduce step by step some symbolic neural network algorithms with the numerical algorithms. +For beginning, a precompilation (or may be caching) of all requirement like shape / strides / slices can be done without recreating them each time and its my first objectif. I will let you being informed on my progress + +### Comment 10 by @aidevnn (2019-08-03T02:50:11Z) + +I forget to comment an important thing about some counters. +``` +var a = ND.Uniform(1, 10, 4, 4); // Counters. DataAccess:16 / MethCall:16 +var b = 3 * ND.Sq(a - 1) - 4; // Counters. DataAccess:16 / MethCall:80 +var c = 3 * ND.Sq(a - 1) - 4 * ND.Sqrt(a + 5); // Counters. DataAccess:32 / MethCall:144 +``` +In the above expression, my actual code and symbolic approch reduce rawdata access only to one time for expression b. Also in a more complexe expression, the lazy loading reduce the data access to the minimum effort for expression c. But i am trying to solve some performances problems. + +We can say that a lazy loading pattern can improve the performance in theory but it introduces some other execution stack problems. I will continue to search how to take advantage of it. + +### Comment 11 by @aidevnn (2019-09-25T17:58:40Z) + +I discovered in this article +https://techdecoded.intel.io/resources/parallelism-in-python-directing-vectorization-with-numexpr/#gs.63gvwl + +Numexpr : it is very usefull and it already use lazy-loading and a lot of symbolics optimisations to enhance performance. diff --git a/docs/issues/issue-0340-memory-limitations.md b/docs/issues/issue-0340-memory-limitations.md new file mode 100644 index 000000000..fd4336097 --- /dev/null +++ b/docs/issues/issue-0340-memory-limitations.md @@ -0,0 +1,31 @@ +# #340: Memory Limitations + +- **URL:** https://github.com/SciSharp/NumSharp/issues/340 +- **State:** OPEN +- **Author:** @Nucs +- **Created:** 2019-08-08T15:39:23Z +- **Updated:** 2019-08-12T13:39:23Z +- **Labels:** enhancement +- **Assignees:** @Nucs + +## Description + +Allocation currently supports up to 2^32 bytes due to using int and not IntPtr and long. + +## Comments + +### Comment 1 by @Nucs (2019-08-08T15:52:05Z) + +Unit test +```C# +[TestMethod] +public void MyTestMethod() +{ + NDArray x = np.zeros(new Shape(600, 1000, 1000), np.float32); +} +``` + +### Comment 2 by @Nucs (2019-08-12T13:38:48Z) + +Ported UnmanagedMemoryBlock and ArraySlice to use long in commit https://github.com/SciSharp/NumSharp/commit/539683f23af53a8b1e31023f8472880cbcc69517 . +Next is to port Shape to use long and all the algorithms with it. Mainly refactoring job diff --git a/docs/issues/issue-0341-ndarray-string-problem.md b/docs/issues/issue-0341-ndarray-string-problem.md new file mode 100644 index 000000000..1b6951bb7 --- /dev/null +++ b/docs/issues/issue-0341-ndarray-string-problem.md @@ -0,0 +1,30 @@ +# #341: NDArray string problem + +- **URL:** https://github.com/SciSharp/NumSharp/issues/341 +- **State:** OPEN +- **Author:** @lokinfey +- **Created:** 2019-08-26T02:39:33Z +- **Updated:** 2019-08-28T21:45:29Z +- **Labels:** missing feature/s +- **Assignees:** @Nucs + +## Description + +I try to use + var bb8List = new NDArray(typeof(string) , new Shape(bb8Num)); + +but it show error like this + +Exception has occurred: CLR/System.NotSupportedException +An unhandled exception of type 'System.NotSupportedException' occurred in NumSharp.Core.dll: 'Specified method is not supported.' + at NumSharp.Backends.Unmanaged.ArraySlice.Allocate(Type elementType, Int32 count, Boolean fillDefault) + at NumSharp.Backends.UnmanagedStorage.Allocate(Shape shape, Type dtype, Boolean fillZeros) + at NumSharp.NDArray..ctor(Type dtype, Shape shape) + at TFDemo.Program.Main(String[] args) in /Users/lokinfey/Desktop/File/Proj/AI/tensorflownet/code/TFDemo/Program.cs:line 61 + +## Comments + +### Comment 1 by @Nucs (2019-08-26T13:02:40Z) + +We currently do not support NDArray of string because `System.String` is not an unmanaged object which causes multiple problems with our current backend architecture since strings can have different sizes. +We do plan to implement it ASAP. diff --git a/docs/issues/issue-0343-built-in-system.drawing.image-and-bitmap-methods.md b/docs/issues/issue-0343-built-in-system.drawing.image-and-bitmap-methods.md new file mode 100644 index 000000000..179abfaec --- /dev/null +++ b/docs/issues/issue-0343-built-in-system.drawing.image-and-bitmap-methods.md @@ -0,0 +1,44 @@ +# #343: Built-in System.Drawing.Image and Bitmap methods + +- **URL:** https://github.com/SciSharp/NumSharp/issues/343 +- **State:** OPEN +- **Author:** @Nucs +- **Created:** 2019-09-01T13:01:21Z +- **Updated:** 2019-11-05T17:00:36Z +- **Labels:** enhancement +- **Assignees:** @Nucs + +## Description + +Theres a repeating need for methods to load Image to bitmap, we should provide performant builtin API for that. + +EDIT 1: +System.Drawing.Bitmap are now supported by a separate package, [read more](https://github.com/SciSharp/NumSharp/wiki/Bitmap-Extensions). + +## Comments + +### Comment 1 by @Oceania2018 (2019-09-01T13:06:07Z) + +Yes, many people need it. It helps. But that will make NumSharp introduce extra dependency. Or we just add file.read to bytes interface ? + +### Comment 2 by @Nucs (2019-09-01T13:09:39Z) + +Yes, `System.Drawing.Image` package. +I'm thinking about `new NDArray(System.Drawing.Image)` and `np.array(System.Drawing.Image)` or something of that sort. + +### Comment 3 by @sdg002 (2019-09-10T09:02:37Z) + +Thanks for all the hard work. +My 2 cents. It might be worth considering the pros and cons of keeping `System.Drawing.Image` outside of the core `NDArray` through a factory approach. At some later date you might consider introducing a factory class for images loaded using `ImageSharp` or some other imaging library. + +This approach will not cut down your code, however it might spare end developers from having to add too many NUGET references. Gives them the opportunity to progressively encompass more packages as the needs grow. E.g. In my company, `System.Drawing` is not really favored because it does not work on Azure functions due to GDI+ restrictions of Azure function sandbox. + + +### Comment 4 by @Nucs (2019-09-11T20:30:52Z) + +@sdg002 Thanks for the note, +I've decided to create separate projects and nuget packages for `NumSharp.Bitmap` (published) and `NumSharp.ImageSharp` (WIP). + +### Comment 5 by @Oceania2018 (2019-10-04T11:11:03Z) + +Is it possible to integrate OpenCvSharp into NumSharp.ImageSharp? diff --git a/docs/issues/issue-0349-scipy.signal.md b/docs/issues/issue-0349-scipy.signal.md new file mode 100644 index 000000000..3ad376eab --- /dev/null +++ b/docs/issues/issue-0349-scipy.signal.md @@ -0,0 +1,90 @@ +# #349: Scipy.Signal + +- **URL:** https://github.com/SciSharp/NumSharp/issues/349 +- **State:** OPEN +- **Author:** @natank1 +- **Created:** 2019-09-21T07:53:55Z +- **Updated:** 2019-09-22T15:17:25Z +- **Labels:** missing feature/s + +## Description + +Hi + +Is there a way to run spectogram with scipy.signal in Using this library? + +## Comments + +### Comment 1 by @Nucs (2019-09-21T09:38:28Z) + +No, Unfortunately we do not have implementations of [scipy.signal](https://docs.scipy.org/doc/scipy/reference/signal.html). +I would suggest you to use [Numpy.NET](https://github.com/SciSharp/Numpy.NET) as it wraps numpy directly and provides all its features. + +### Comment 2 by @natank1 (2019-09-21T09:48:29Z) + +Including SciPy ? + +### Comment 3 by @Nucs (2019-09-21T10:43:05Z) + +Scipy is the name of the company that developed numpy... +אנחנו תומכים בעיקר באלגברה לינארית שזה בערך השימוש הכי נפוץ בסיפריה והכרחי בכדי להפעיל את הסיפריה טנסורפלוו +הספריה [נמפיינט ](https://github.com/SciSharp/Numpy.NET) תומכת בכל הפיצ'רים של נמפיי + +### Comment 4 by @natank1 (2019-09-21T13:03:15Z) + +Sorry again + +When you say the Scipy exists you mean we have an API for it under +Numpy.Net (I can find such ), +or we can take the python code of scipy and arrange it in away that +Numpy.net will handle this? +Sorry again fro bothering + +On Sat, Sep 21, 2019 at 12:38 PM Eli Belash +wrote: + +> No, Unfortunately we do not have implementations of scipy.signal +> . +> I would suggest you to use Numpy.NET +> as it wraps numpy directly and +> provides all its features. +> +> — +> You are receiving this because you authored the thread. +> Reply to this email directly, view it on GitHub +> , +> or mute the thread +> +> . +> + + +### Comment 5 by @Nucs (2019-09-21T14:06:35Z) + +Scipy is the name of the company that published Numpy. When you say Scipy it makes no sense. +When I comment I add URLs, just click on my mentions in the comments above of numpy.net or scipy.signal. +Numpy.NET is a different library from NumSharp which uses Pythonnet to call numpy. +Numpy.NET implements ALL numpy's functions but might be sometimes slower because you transfer data from C# to python. + +### Comment 6 by @natank1 (2019-09-21T14:36:13Z) + +So function /class that is written in python sipy.signal , how should be called inumpy.net? + +> Scipy is the name of the company that published Numpy. When you say Scipy it makes no sense. +> When I comment I add URLs, just click on my mentions of numpy.net. +> Numpy.NET is a different library from NumSharp which uses Pythonnet to call numpy. +> Numpy.NET implements ALL numpy's functions but might be sometimes slower because you transfer data from C# to python. +> +> — +> You are receiving this because you authored the thread. +> Reply to this email directly, view it on GitHub, or mute the thread. + + +### Comment 7 by @Nucs (2019-09-21T15:57:10Z) + +I have no clue, This is NumSharp repository. +Read Numpy.NET's readme.md. + +### Comment 8 by @natank1 (2019-09-21T16:06:56Z) + +Thanks! diff --git a/docs/issues/issue-0351-proper-way-to-iterate-using-ienumerable-t.md b/docs/issues/issue-0351-proper-way-to-iterate-using-ienumerable-t.md new file mode 100644 index 000000000..b9f655cd6 --- /dev/null +++ b/docs/issues/issue-0351-proper-way-to-iterate-using-ienumerable-t.md @@ -0,0 +1,15 @@ +# #351: Proper way to iterate using IEnumerable + +- **URL:** https://github.com/SciSharp/NumSharp/issues/351 +- **State:** OPEN +- **Author:** @Nucs +- **Created:** 2019-09-28T15:42:47Z +- **Updated:** 2019-09-28T15:42:47Z +- **Labels:** enhancement +- **Assignees:** @Nucs + +## Description + +There should be an approachable way to perform fast `foreach` on a `NDArray`. +Currently `NDArray` implements non-generic `IEnumerable` which returns a boxed value that can be either the `NDArray.dtype` or an `NDArray`. +Boxing causes `O(n)` operations to be significantly slower on large datasets. diff --git a/docs/issues/issue-0360-np.any.md b/docs/issues/issue-0360-np.any.md new file mode 100644 index 000000000..5499f7e25 --- /dev/null +++ b/docs/issues/issue-0360-np.any.md @@ -0,0 +1,21 @@ +# #360: np.any + +- **URL:** https://github.com/SciSharp/NumSharp/issues/360 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2019-10-05T23:50:58Z +- **Updated:** 2019-10-12T11:16:22Z +- **Labels:** enhancement +- **Assignees:** @Nucs + +## Description + +Test whether any array element along a given axis evaluates to True. +https://docs.scipy.org/doc/numpy/reference/generated/numpy.any.html + +## Comments + +### Comment 1 by @Nucs (2019-10-12T11:16:22Z) + +Added in https://github.com/SciSharp/NumSharp/commit/f4ee1353e1d159a9e9334dcb38ee4f005160808c , +Remaining open because specific axis support is not implemented yet. diff --git a/docs/issues/issue-0361-mixing-indices-and-slices-in-ndarray.md b/docs/issues/issue-0361-mixing-indices-and-slices-in-ndarray.md new file mode 100644 index 000000000..da00e0aac --- /dev/null +++ b/docs/issues/issue-0361-mixing-indices-and-slices-in-ndarray.md @@ -0,0 +1,101 @@ +# #361: Mixing indices and slices in NDArray[...] + +- **URL:** https://github.com/SciSharp/NumSharp/issues/361 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2019-10-06T03:22:07Z +- **Updated:** 2019-10-12T11:35:37Z +- **Labels:** enhancement +- **Assignees:** @henon, @Nucs + +## Description + +Is it possible to suppor mixed index/ slice? This is what numpy doing: + +![image](https://user-images.githubusercontent.com/1705364/66263777-49d4dc00-e7be-11e9-8fba-fd1014cbd922.png) + +Currently, it doesn't support: + +![image](https://user-images.githubusercontent.com/1705364/66263800-7688f380-e7be-11e9-933a-2dfeb71f98f3.png) + + +## Comments + +### Comment 1 by @Nucs (2019-10-06T06:31:04Z) + +```C# +label[i][Slice.Index(yind), Slice.Index(xind), Slice.Index(iou_mast), new Slice(0, 4)] = bbox_xywh; +``` + +### Comment 2 by @henon (2019-10-06T11:48:20Z) + +or + +```C# +label[i][$"{yind}, {xind}, {iou_mast}, 0:4"] = bbox_xywh; +``` + +Btw: we could try implicitly convert from int to Slice.Index(int) + + +### Comment 3 by @Nucs (2019-10-06T12:30:22Z) + +Worth mentioning that using a string inside a loop or O(n) will affect performance. +Regex parsing is not lightning fast. + +### Comment 4 by @Oceania2018 (2019-10-06T13:51:40Z) + +The `iou_mask` is `NDArray`. +![image](https://user-images.githubusercontent.com/1705364/66269842-048ec980-e813-11e9-8e33-fa624271ace2.png) + +It means we use different ways in different dimension to select elements. + +One approach my idea is: +Define a `IIndex` interface, `NDArray` and `Slice` implement `IIndex`. +update +```csharp +NDArray this[params IIndex[] selectors] +{ + get; set; +} +``` + + +### Comment 5 by @Nucs (2019-10-06T14:40:04Z) + +Too messy, plus it won't solve your problem.. you can't implement implicit cast from `int` to `IIndex`. +Having index `nd[int, int, NDArray, Slice]` doesn't make sense. Our algorithm accepts either only slices or only ints. +`nd[NDArray[]]` is not for Indexing, it is for Masking. +Right now use `Slice.Index`, in the future I'll implement implicit castings to `Slice`. + +### Comment 6 by @henon (2019-10-06T15:04:49Z) + +We might have to extend the slicing algorithm to accept a mask (i.e. Slice.Mask(boolean_array)). He is porting Python code so it seems Python allows mixing of indices and masks. + +### Comment 7 by @Nucs (2019-10-06T15:06:57Z) + +Masking has a complete separate algorithm from slicing. In fact they don't have anything in common (function-wise). + +### Comment 8 by @Oceania2018 (2019-10-06T16:36:44Z) + +Not only for masking, but also for any 1d array. We don’t have to have int to IIndex implicitly, we can use np.array(1) as alternative. + +The goal is having people can use 1d or Slice, Scalar to select and mask elements in appropriate dimensions. + +### Comment 9 by @Oceania2018 (2019-10-12T00:13:21Z) + +@henon This situation doesn't work: + +`iou_mask` is `NDArray`, `yind` and `xind` are `int`: +![image](https://user-images.githubusercontent.com/1705364/66691314-0addd500-ec5b-11e9-816f-be0dd63f9d0a.png) + +![image](https://user-images.githubusercontent.com/1705364/66691447-0fef5400-ec5c-11e9-8368-d9f01b5ea440.png) + + +### Comment 10 by @henon (2019-10-12T08:32:57Z) + +I know, @Nucs is removing the int[] indexer in favor of an object[] indexer so that implicit conversion operators don't get in the way and we can reliably forward the different use cases into the right implementatons ( slicing if no NDarrays are part of the params and index extraction/masking otherwise) + +### Comment 11 by @Nucs (2019-10-12T11:35:37Z) + +@Oceania2018 Please verify if it works on master branch. diff --git a/docs/issues/issue-0362-implicit-operators-for.md b/docs/issues/issue-0362-implicit-operators-for.md new file mode 100644 index 000000000..093c12fa5 --- /dev/null +++ b/docs/issues/issue-0362-implicit-operators-for.md @@ -0,0 +1,43 @@ +# #362: Implicit operators for >, >=, <, <= + +- **URL:** https://github.com/SciSharp/NumSharp/issues/362 +- **State:** OPEN +- **Author:** @deepakkumar1984 +- **Created:** 2019-10-06T07:49:57Z +- **Updated:** 2019-10-12T00:33:30Z +- **Labels:** help wanted, missing feature/s + +## Description + +x == 1 gives a result which is NDArray as expected + +x>1 and other operators like >=, <, <= give null result + +## Comments + +### Comment 1 by @Nucs (2019-10-06T07:51:06Z) + +`np.where` is yet to be implemented. + +### Comment 2 by @deepakkumar1984 (2019-10-06T08:55:19Z) + +I have done some work with np.where in this commit https://github.com/deepakkumar1984/NumSharp/commit/8bfa4f2484fc974e10e250f6b7ef5da0f381b683 +the first parameter is a condition for which we need to finish the implementation under https://github.com/SciSharp/NumSharp/tree/master/src/NumSharp.Core/Operations/Elementwise + +Most of the code in commented and return as null + +### Comment 3 by @Nucs (2019-10-06T09:03:06Z) + +I misunderstood [np.where](https://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html), I was sure that the first argument is something of a sort of Expression>. +I'll need to implement each (comparing) operator separately and np.where will work then. +I'll open up a separate issue for that. This might take a while as I'm unavailable (sunday is not a day off in my country 😅) + +### Comment 4 by @Oceania2018 (2019-10-12T00:03:42Z) + +Same thing happened for me, shouldn't return null: +![image](https://user-images.githubusercontent.com/1705364/66691132-c6056e80-ec59-11e9-890e-29e516a00a1f.png) + + +### Comment 5 by @Nucs (2019-10-12T00:33:30Z) + +Null because it is not implemented. diff --git a/docs/issues/issue-0363-add-nditerator-t-overload-with-support-for-specific-axis.md b/docs/issues/issue-0363-add-nditerator-t-overload-with-support-for-specific-axis.md new file mode 100644 index 000000000..bb99dbd86 --- /dev/null +++ b/docs/issues/issue-0363-add-nditerator-t-overload-with-support-for-specific-axis.md @@ -0,0 +1,18 @@ +# #363: Add `NDIterator` overload with support for specific axis. + +- **URL:** https://github.com/SciSharp/NumSharp/issues/363 +- **State:** OPEN +- **Author:** @Nucs +- **Created:** 2019-10-12T11:11:32Z +- **Updated:** 2019-10-12T11:11:32Z +- **Labels:** missing feature/s + +## Description + +NDIterator is useful, we should add an overload that handles specific axis iterator: +usage: +```C# +new NDIterator(ndarray, axis: 1); +``` + +Also add an overload to extension `ndarray.AsIterator(axis: 1);` diff --git a/docs/issues/issue-0365-np.nonzero.md b/docs/issues/issue-0365-np.nonzero.md new file mode 100644 index 000000000..c34d85e93 --- /dev/null +++ b/docs/issues/issue-0365-np.nonzero.md @@ -0,0 +1,14 @@ +# #365: np.nonzero + +- **URL:** https://github.com/SciSharp/NumSharp/issues/365 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2019-10-12T19:49:12Z +- **Updated:** 2019-10-12T19:49:12Z +- **Labels:** enhancement +- **Assignees:** @Nucs + +## Description + +Return the indices of the elements that are non-zero. +https://docs.scipy.org/doc/numpy/reference/generated/numpy.nonzero.html diff --git a/docs/issues/issue-0366-masking-ndarray-nd.md b/docs/issues/issue-0366-masking-ndarray-nd.md new file mode 100644 index 000000000..9da897abf --- /dev/null +++ b/docs/issues/issue-0366-masking-ndarray-nd.md @@ -0,0 +1,26 @@ +# #366: Masking (ndarray[nd]) + +- **URL:** https://github.com/SciSharp/NumSharp/issues/366 +- **State:** OPEN +- **Author:** @henon +- **Created:** 2019-10-12T19:49:33Z +- **Updated:** 2019-10-12T19:51:59Z + +## Description + +Doesn't work in ndarray[nd]? + +![image](https://user-images.githubusercontent.com/1705364/66706800-4335ef80-ecfd-11e9-9a4d-4631255241d8.png) + +```shell +System.NotSupportedException: Specified method is not supported. + at NumSharp.NDArray.set_Item(Object[] indices_or_slices, NDArray value) in D:\SciSharp\NumSharp\src\NumSharp.Core\Selection\NDArray.Indexing.cs:line 202 +``` + +_Originally posted by @Oceania2018 in https://github.com/SciSharp/NumSharp/issues/359#issuecomment-541355337_ + +## Comments + +### Comment 1 by @henon (2019-10-12T19:51:59Z) + +we need this for it: #365 diff --git a/docs/issues/issue-0368-masking-a-slice-...-returns-null.md b/docs/issues/issue-0368-masking-a-slice-...-returns-null.md new file mode 100644 index 000000000..da40d4e51 --- /dev/null +++ b/docs/issues/issue-0368-masking-a-slice-...-returns-null.md @@ -0,0 +1,45 @@ +# #368: Masking a slice ("...") returns null + +- **URL:** https://github.com/SciSharp/NumSharp/issues/368 +- **State:** OPEN +- **Author:** @ohjerm +- **Created:** 2019-11-12T09:48:54Z +- **Updated:** 2019-11-12T14:56:29Z +- **Assignees:** @henon, @Nucs + +## Description + +``` +NDArray positive_boxes = batch_item["...,0"] != 0; +Debug.Log(positive_boxes); +``` +The output is Null. In numpy I would expect an boolean mask NDArray of shape (...,1) here, right? Is masking implemented another way? + +I'm on the latest nuget release (0.20.4) if that helps. + +## Comments + +### Comment 1 by @henon (2019-11-12T14:16:57Z) + +@ChaiKnight: I can not work with the example you have given here to reproduce the problem. Can you please write me a piece of code that is complete and reproduces the bug? + +Like this: + +```C# +var x=np.arange(10).reshape(2,5); +var y=x["...,0"]; +// what does it give and what do you expect instead? +``` +I don't know your setup, the above is just an example. Thanks. + +### Comment 2 by @ohjerm (2019-11-12T14:42:55Z) + +I don't know how much more I can give you without just dumping my entire codebase on you. My batch_item is an NDArray of size (7160,6). When I test I currently just feed it zeroes, so it basically looks like np.zero((7160,6)) to me when I print it. + +Your example seems to work just fine, I am able to print out [0,5], but I am also able to print out a long array of zeroes when I do `Debug.Log(batch_item["...,0"]);`, so somewhere in the masking process is where it goes wrong. I figured the masking should return an empty array like numpy does, of size (0,6). Is that at all possible? + + + +### Comment 3 by @henon (2019-11-12T14:55:51Z) + +Alright, I understand. This is a known problem then. Masking is currently under construction. @Nucs is working on it. diff --git a/docs/issues/issue-0369-slicing-notsupportedexception.md b/docs/issues/issue-0369-slicing-notsupportedexception.md new file mode 100644 index 000000000..6e7c1a1f1 --- /dev/null +++ b/docs/issues/issue-0369-slicing-notsupportedexception.md @@ -0,0 +1,15 @@ +# #369: Slicing NotSupportedException + +- **URL:** https://github.com/SciSharp/NumSharp/issues/369 +- **State:** OPEN +- **Author:** @Oceania2018 +- **Created:** 2019-11-23T18:48:33Z +- **Updated:** 2019-11-23T18:48:33Z +- **Labels:** missing feature/s +- **Assignees:** @Nucs + +## Description + +Runing YOLO with `ndarray-indexing` branch. +![image](https://user-images.githubusercontent.com/1705364/69483632-54d7e000-0def-11ea-99b4-3b2863490b39.png) + diff --git a/docs/issues/issue-0372-clustering-example.md b/docs/issues/issue-0372-clustering-example.md new file mode 100644 index 000000000..3ff0dc3c8 --- /dev/null +++ b/docs/issues/issue-0372-clustering-example.md @@ -0,0 +1,24 @@ +# #372: Clustering Example + +- **URL:** https://github.com/SciSharp/NumSharp/issues/372 +- **State:** OPEN +- **Author:** @turowicz +- **Created:** 2019-12-05T13:32:43Z +- **Updated:** 2019-12-07T17:51:04Z +- **Labels:** help wanted, missing feature/s + +## Description + +Do we have a C# version of something like this? +https://github.com/lars76/kmeans-anchor-boxes + + +## Comments + +### Comment 1 by @turowicz (2019-12-05T13:48:45Z) + +cc @Nucs @Oceania2018 + +### Comment 2 by @turowicz (2019-12-05T14:13:32Z) + +It seems that C# `np.random.choice` has different arguments than the original one. diff --git a/docs/issues/issue-0373-np.median.md b/docs/issues/issue-0373-np.median.md new file mode 100644 index 000000000..75583bced --- /dev/null +++ b/docs/issues/issue-0373-np.median.md @@ -0,0 +1,18 @@ +# #373: np.median + +- **URL:** https://github.com/SciSharp/NumSharp/issues/373 +- **State:** OPEN +- **Author:** @turowicz +- **Created:** 2019-12-05T13:42:11Z +- **Updated:** 2019-12-07T17:49:43Z +- **Labels:** help wanted, missing feature/s + +## Description + +What is the np.median equivalent? + +## Comments + +### Comment 1 by @turowicz (2019-12-05T13:48:32Z) + +cc @Nucs @Oceania2018 diff --git a/docs/issues/issue-0374-np.append.md b/docs/issues/issue-0374-np.append.md new file mode 100644 index 000000000..f7aa8f78e --- /dev/null +++ b/docs/issues/issue-0374-np.append.md @@ -0,0 +1,19 @@ +# #374: np.append + +- **URL:** https://github.com/SciSharp/NumSharp/issues/374 +- **State:** OPEN +- **Author:** @solarflarefx +- **Created:** 2019-12-05T20:29:23Z +- **Updated:** 2019-12-07T17:49:30Z +- **Labels:** help wanted, missing feature/s + +## Description + +Is there an equivalent method to np.append? If not, what is the best workaround? + +## Comments + +### Comment 1 by @Nucs (2019-12-07T17:49:22Z) + +Maybe np.concatenate, np.vstack or np.stack will help you. They might require reshaping to emulate np.append. + diff --git a/docs/issues/issue-0375-slice-assignment.md b/docs/issues/issue-0375-slice-assignment.md new file mode 100644 index 000000000..f4225f8a5 --- /dev/null +++ b/docs/issues/issue-0375-slice-assignment.md @@ -0,0 +1,25 @@ +# #375: Slice assignment? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/375 +- **State:** OPEN +- **Author:** @solarflarefx +- **Created:** 2019-12-07T17:40:06Z +- **Updated:** 2019-12-07T17:48:07Z +- **Labels:** missing feature/s +- **Assignees:** @Nucs + +## Description + +If you declare an array of a specific size, is there a way to assign slices of arrays? + +For example, if x is an NDArray of size (5,2,3,4), is there a way to do something like the following? + +NDArray a = some NDArray of size (1,2,3,4) + +x[0,:,:,:] = a + +## Comments + +### Comment 1 by @Nucs (2019-12-07T17:47:58Z) + +Related to https://github.com/SciSharp/NumSharp/issues/369 https://github.com/SciSharp/NumSharp/issues/368 https://github.com/SciSharp/NumSharp/issues/366 and is work in progress. diff --git a/docs/issues/issue-0378-add-np.frombuffer.md b/docs/issues/issue-0378-add-np.frombuffer.md new file mode 100644 index 000000000..881088ff7 --- /dev/null +++ b/docs/issues/issue-0378-add-np.frombuffer.md @@ -0,0 +1,14 @@ +# #378: Add np.frombuffer + +- **URL:** https://github.com/SciSharp/NumSharp/issues/378 +- **State:** OPEN +- **Author:** @Nucs +- **Created:** 2019-12-15T19:39:07Z +- **Updated:** 2019-12-15T19:39:07Z +- **Labels:** enhancement, missing feature/s +- **Assignees:** @Nucs + +## Description + +This is how we should wrap a pointer to a certain memory block. +https://docs.scipy.org/doc/numpy/reference/generated/numpy.frombuffer.html diff --git a/docs/issues/issue-0383-is-there-any-way-to-convert-numsharp.ndarray-to-numpy.ndarray.md b/docs/issues/issue-0383-is-there-any-way-to-convert-numsharp.ndarray-to-numpy.ndarray.md new file mode 100644 index 000000000..f2201a0e6 --- /dev/null +++ b/docs/issues/issue-0383-is-there-any-way-to-convert-numsharp.ndarray-to-numpy.ndarray.md @@ -0,0 +1,94 @@ +# #383: is there any way to convert NumSharp.NDArray to Numpy.NDarray? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/383 +- **State:** OPEN +- **Author:** @lelelemonade +- **Created:** 2020-01-06T10:31:01Z +- **Updated:** 2023-02-24T10:15:20Z + +## Description + +_No description provided._ + +## Comments + +### Comment 1 by @Nucs (2020-01-06T11:15:15Z) + +Me and @henon have collaborated just two days ago on a solution for that, +The trick is to create a python scope, load a script that takes in a pointer-length and returns an np.ndarray +Remember! you have to keep reference to the original NDArray, otherwise the NDArray will be disposed and will release the memory. (@henon is there a tag field we can use in NDarray?) + +Note: the code below is psuedo, the rest of the code can be found here: [ConsoleApp9.zip](https://github.com/SciSharp/NumSharp/files/4025804/ConsoleApp9.zip) + + +*numpy_interop.py* as embedded resource +```python +import numpy as np +import ctypes + +def to_numpy(ptr, bytes, dtype, shape): + return np.frombuffer((ctypes.c_uint8*(bytes)).from_address(ptr), dtype).reshape(shape) + +``` + +*PythonExtensions.cs* +```C# +public static class PythonExtensions { + + ... + private static PyScope NumpyInteropScope; + private static PyObject NumpyConverter; + + public static unsafe NDarray ToNumpyNET(this NDArray nd) { + using (Py.GIL()) { + if (NumpyInteropScope == null) { + NumpyInteropScope = Py.CreateScope(); + NumpyInteropScope.ExecResource("numpy_interop.py"); + NumpyConverter = NumpyInteropScope.GetFunction("to_numpy"); + } + + return new NDarray(NumpyConverter.Invoke(new PyLong((long) nd.Unsafe.Address), + new PyLong(nd.size * nd.dtypesize), + new PyString(nd.dtype.Name.ToLowerInvariant()), + new PyTuple(nd.shape.Select(dim => (PyObject) new PyLong(dim)).ToArray()))); + } + } +} +``` + +*main.cs* +```C# +using numsharp_np = NumSharp.np; +using numpy_np = Numpy.np; +static void Main(string[] args) { + var numsharp_nd = numsharp_np.arange(10); + var numpy_nd = numsharp_nd.ToNumpyNET(); + numpy_nd[1] = (NDarray) 5; + Debug.Assert(numsharp_nd[1].array_equal(5)); + Console.WriteLine("numsharp_nd[1].array_equal(5) is indeed true!"); +} +``` + +### Comment 2 by @Nucs (2020-01-06T11:35:04Z) + +Added some changes that will make sure the NDarray will hold reference to the NDArray as long as the PyObject doesnt change internally. + +[ConsoleApp9.zip](https://github.com/SciSharp/NumSharp/files/4025864/ConsoleApp9.zip) + + + +### Comment 3 by @henon (2020-01-06T15:13:53Z) + +> Remember! you have to keep reference to the original NDArray, otherwise the NDArray will be disposed and will release the memory. (@henon is there a tag field we can use in NDarray?) +> + +Not yet, but I will add one in Numpy.NET and support this. + + +### Comment 4 by @davidvct (2023-02-24T04:14:22Z) + +Hi, could you provide instruction on how to use the ConsoleApp9.zip? And does it able to convert Numpy array back to NumSharp array? + +### Comment 5 by @henon (2023-02-24T10:15:20Z) + +@davidvct, if I remember correctly I have added support for copying data to and from Numpy.NET like @Nucs did in this solution. Read this to see how: https://github.com/SciSharp/Numpy.NET#create-a-numpy-array-from-a-c-array-and-vice-versa diff --git a/docs/issues/issue-0384-save-ndarray-as-png-image.md b/docs/issues/issue-0384-save-ndarray-as-png-image.md new file mode 100644 index 000000000..b640605e1 --- /dev/null +++ b/docs/issues/issue-0384-save-ndarray-as-png-image.md @@ -0,0 +1,43 @@ +# #384: Save NDArray as png image + +- **URL:** https://github.com/SciSharp/NumSharp/issues/384 +- **State:** OPEN +- **Author:** @solarflarefx +- **Created:** 2020-01-12T23:54:22Z +- **Updated:** 2020-01-13T00:30:55Z + +## Description + +I am looking to save an NDArray to an image. + +In Python code, I used the io.imsave() method from skimage.io. + +I tried using the approach shown here: https://stackoverflow.com/questions/5113919/how-to-convert-2-d-array-into-image-in-c-sharp + +Basically it uses the the Bitmap method in System.Drawing.Bitmap + +Is this the correct way to do this? + +I tried converting the NDArray to a C# multidimensional array and then using the following type of code: + +`Bitmap bitmap; +unsafe +{ + fixed (int* intPtr = &integers[0,0]) + { + bitmap = new Bitmap(width, height, stride, PixelFormat.Format32bppRgb, new IntPtr(intPtr)); + } +}` + +However, I get this error on the "bitmap =" line: System.ArgumentException: 'Parameter is not valid.' + +Ultimately I would like to compare the output from my python code to the output from my C# code to ensure that they are doing the same thing. As I stated, in Python I saved a multidimensional array to png. My thought was to do the same in C# using NumSharp, and then comparing the output images. + +Thanks in advance. + +## Comments + +### Comment 1 by @Nucs (2020-01-13T00:30:00Z) + +Take a look at our [wiki](https://github.com/SciSharp/NumSharp/wiki/Bitmap-Extensions). We provide support for converting/wrapping NDArray to System.Drawing.Bitmap. +If it is for the purpose of only saving then specify argument `copy: false` diff --git a/docs/issues/issue-0386-how-to-read-.csv-file-with-numsharp.md b/docs/issues/issue-0386-how-to-read-.csv-file-with-numsharp.md new file mode 100644 index 000000000..bbea2df8e --- /dev/null +++ b/docs/issues/issue-0386-how-to-read-.csv-file-with-numsharp.md @@ -0,0 +1,18 @@ +# #386: how to read .csv file with Numsharp? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/386 +- **State:** OPEN +- **Author:** @guang7400613 +- **Created:** 2020-01-15T13:56:28Z +- **Updated:** 2020-01-15T14:09:27Z + +## Description + +how to read .csv file or .txt file wiht Numsharp(np) in C#? +is it np.load(string filename)? + +## Comments + +### Comment 1 by @Oceania2018 (2020-01-15T14:09:27Z) + +Should use Pandas.NET diff --git a/docs/issues/issue-0390-how-to-create-an-ndarray-from-pointer-and-nptypecode.md b/docs/issues/issue-0390-how-to-create-an-ndarray-from-pointer-and-nptypecode.md new file mode 100644 index 000000000..d82527ef3 --- /dev/null +++ b/docs/issues/issue-0390-how-to-create-an-ndarray-from-pointer-and-nptypecode.md @@ -0,0 +1,52 @@ +# #390: How to create an NDArray from pointer and NPTypeCode? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/390 +- **State:** OPEN +- **Author:** @LarryThermo +- **Created:** 2020-01-24T19:52:51Z +- **Updated:** 2020-01-27T17:13:48Z + +## Description + +First of all thanks for this project. + +My code calls into lower level C++ code and I wish to represent the returned array as an NDArray in C#. I have a pointer to the data, the length of the data in bytes, as well as type code. How would I go about creating an NDArray for this case? Something like: + +NDArray CreateNDArray(void* data,int dataLengthBytes,NPTypeCode typeCode) +{ +var storage = new UnmanagedStorage( "something here" ); +var nda = new NDArray(storage); +return nda; +} + +Thanks, + +Lars + +## Comments + +### Comment 1 by @Oceania2018 (2020-01-24T23:19:00Z) + +@LarryThermo This [snippet of code](https://github.com/SciSharp/SharpCV/blob/792c8744856cff69f06ddfb17c2866d4d18a05db/src/SharpCV/Core/Mat.cs#L128) will help you. + +### Comment 2 by @LarryThermo (2020-01-27T17:13:48Z) + +Thank you. It was helpful. + +Although this works for me I was hoping to find a solution that did not involve maintaining a case statement for all possible types, and instead took in a void* pointer and an NPTypeCode. + +From: Haiping [mailto:notifications@github.com] +Sent: Friday, January 24, 2020 3:19 PM +To: SciSharp/NumSharp +Cc: Rystrom, Larry ; Mention +Subject: Re: [SciSharp/NumSharp] How to create an NDArray from pointer and NPTypeCode? (#390) + +CAUTION: This email originated from outside of Thermo Fisher Scientific. If you believe it to be suspicious, report using the Report Phish button in Outlook or send to SOC@thermofisher.com. + + +@LarryThermo This snippet of code will help you. + +— +You are receiving this because you were mentioned. +Reply to this email directly, view it on GitHub, or unsubscribe. + diff --git a/docs/issues/issue-0396-bitmap.tondarray-problem-with-odd-bitmap-width.md b/docs/issues/issue-0396-bitmap.tondarray-problem-with-odd-bitmap-width.md new file mode 100644 index 000000000..af5d4e902 --- /dev/null +++ b/docs/issues/issue-0396-bitmap.tondarray-problem-with-odd-bitmap-width.md @@ -0,0 +1,40 @@ +# #396: Bitmap.ToNDArray problem with odd bitmap width + +- **URL:** https://github.com/SciSharp/NumSharp/issues/396 +- **State:** OPEN +- **Author:** @herrvonregen +- **Created:** 2020-02-06T12:04:28Z +- **Updated:** 2020-02-06T16:44:23Z + +## Description + +Hi everyone. +The official micorsoft documenation for Bitmap.Stride says: + +> The stride is the width of a single row of pixels (a scan line), rounded up to a four-byte boundary. + +This could cause an exception here if the loaded bitmap is RGB with an odd width. +` Buffer.MemoryCopy(src, dst, bmpData.Stride * image.Height, nd.size);` +`var ret = nd.reshape(1, image.Height, image.Width, bmpData.Stride / bmpData.Width);` + +Example: +An RGB image with the size of 227x227 pixels +Stride will be 684. 227*3 = 681 rounded up to four-byte boundary. +Copied data are 155268 bytes. +Reshaped into 277 * 277 * floor(684/227) = 154587 bytes +This will cause an IncorrectShapeException + + + +## Comments + +### Comment 1 by @Nucs (2020-02-06T15:44:37Z) + +You shouldn't work hard, we provide extensions to Bitmap, see [this wiki article](https://github.com/SciSharp/NumSharp/wiki/Bitmap-Extensions). + +### Comment 2 by @herrvonregen (2020-02-06T16:44:23Z) + +Yes, I know but the implementation has the described behavior. +Try it with the picture provided below. +![00001](https://user-images.githubusercontent.com/33056845/73958447-39697500-4908-11ea-8628-33dce6582f17.jpg) + diff --git a/docs/issues/issue-0397-missing-np.tile.md b/docs/issues/issue-0397-missing-np.tile.md new file mode 100644 index 000000000..0e72047f6 --- /dev/null +++ b/docs/issues/issue-0397-missing-np.tile.md @@ -0,0 +1,64 @@ +# #397: Missing np.tile? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/397 +- **State:** OPEN +- **Author:** @QadiymStewart +- **Created:** 2020-02-12T15:55:12Z +- **Updated:** 2020-06-03T13:42:46Z +- **Assignees:** @Nucs + +## Description + +Is np.tile implemented in this library as of yet? + +## Comments + +### Comment 1 by @simonbuehler (2020-05-25T10:25:11Z) + +hi, + +bumped into the same issue, is there a workaround for something like `np.tile(center, (1, 1, 2 * num_anchors));` or maybe even better a working tile implementation? + +### Comment 2 by @QadiymStewart (2020-05-25T14:41:50Z) + +> hi, +> +> bumped into the same issue, is there a workaround for something like `np.tile(center, (1, 1, 2 * num_anchors));` or maybe even better a working tile implementation? + +Switched to https://github.com/Quansight-Labs/numpy.net + +### Comment 3 by @simonbuehler (2020-05-25T14:56:20Z) + +@QadiymStewart thanks for your reply, a pity that tensorflow.net depends on NumSharp so i can't switch :/ +@Oceania2018 are there plans to implement this like in[NumpyDotNet/shape_base.cs](https://github.com/Quansight-Labs/numpy.net/blob/b6ac4af87e21bd561a022ebe067d322b88273157/src/NumpyDotNet/NumpyDotNet/shape_base.cs#L1623) ? + +### Comment 4 by @Oceania2018 (2020-05-25T15:23:08Z) + +@simonbuehler Try to use the built-in functions of tensorflow. https://www.tensorflow.org/api_docs/python/tf/tile + +### Comment 5 by @simonbuehler (2020-05-25T17:54:25Z) + +is there already a nuget with a Tensor -> NDarray method? + +### Comment 6 by @Oceania2018 (2020-05-26T03:38:36Z) + +@simonbuehler You can create tensor directly from Tensor -> NDArray and vice verse. + +### Comment 7 by @simonbuehler (2020-05-26T08:54:41Z) + +just for info, unfortunatly `var center_tiled = tf.tile(temp , temp2);` throws a `NullReferenceException` guess this is a TensorFlow.NET issue, nevertheless a numsharp implementation would be awesome! + +``` + TensorFlow.NET.dll!Tensorflow.Tensor._as_tf_output() Unbekannt + TensorFlow.NET.dll!Tensorflow.ops._create_c_op(Tensorflow.Graph graph, Tensorflow.NodeDef node_def, object[] inputs, Tensorflow.Operation[] control_inputs) Unbekannt + TensorFlow.NET.dll!Tensorflow.Operation.Operation(Tensorflow.NodeDef node_def, Tensorflow.Graph g, Tensorflow.Tensor[] inputs, Tensorflow.TF_DataType[] output_types, Tensorflow.ITensorOrOperation[] control_inputs, Tensorflow.TF_DataType[] input_types, string original_op, Tensorflow.OpDef op_def) Unbekannt + TensorFlow.NET.dll!Tensorflow.Graph.create_op(string op_type, Tensorflow.Tensor[] inputs, Tensorflow.TF_DataType[] dtypes, Tensorflow.TF_DataType[] input_types, string name, System.Collections.Generic.Dictionary attrs, Tensorflow.OpDef op_def) Unbekannt + TensorFlow.NET.dll!Tensorflow.OpDefLibrary._apply_op_helper.AnonymousMethod__0(Tensorflow.ops.NameScope scope) Unbekannt + TensorFlow.NET.dll!Tensorflow.Binding.tf_with(System.__Canon py, System.Func action) Unbekannt + TensorFlow.NET.dll!Tensorflow.gen_array_ops.tile(Tensorflow.Tensor input, Tensorflow.Tensor multiples, string name) Unbekannt + +``` + +### Comment 8 by @simonbuehler (2020-06-03T13:42:46Z) + +@Oceania2018 hi, are there any chances that np.tile could be implemented? diff --git a/docs/issues/issue-0398-typo-in-library-np.random.stardard.md b/docs/issues/issue-0398-typo-in-library-np.random.stardard.md new file mode 100644 index 000000000..93d5ef039 --- /dev/null +++ b/docs/issues/issue-0398-typo-in-library-np.random.stardard.md @@ -0,0 +1,18 @@ +# #398: Typo in library np.random.stardard + +- **URL:** https://github.com/SciSharp/NumSharp/issues/398 +- **State:** OPEN +- **Author:** @QadiymStewart +- **Created:** 2020-02-12T16:29:28Z +- **Updated:** 2020-02-12T16:29:28Z + +## Description + +Found a typo + np.random.stardard_normal(Batch_Size, X_Res * Y_Res, 1); + +should be + np.random.standard_normal(Batch_Size, X_Res * Y_Res, 1); + +Spelling Error "standard" + diff --git a/docs/issues/issue-0401-how-to-convert-ndarray-to-list.md b/docs/issues/issue-0401-how-to-convert-ndarray-to-list.md new file mode 100644 index 000000000..6c7600fcf --- /dev/null +++ b/docs/issues/issue-0401-how-to-convert-ndarray-to-list.md @@ -0,0 +1,90 @@ +# #401: How to convert NDArray to list + +- **URL:** https://github.com/SciSharp/NumSharp/issues/401 +- **State:** OPEN +- **Author:** @Sullivanecidi +- **Created:** 2020-03-18T00:26:17Z +- **Updated:** 2020-03-30T02:57:02Z + +## Description + +It is really a big surprise for me to find the Numsharp. +Here is little question with using the NumSharp: How to convert the NDArray data to list ? since in VB.net, list is the frequently used. +For example: +dim x_data, y_data as NDArray +dim y() as double +x_data = np.linspace(0,100,500) +y_data = x_data * 2 + np.random.randn(500) + +how to convert y_data to y() ? + +Thanks! + +## Comments + +### Comment 1 by @Oceania2018 (2020-03-18T02:35:02Z) + +`x_data.ToArray()` + +### Comment 2 by @Sullivanecidi (2020-03-18T03:05:49Z) + +> `x_data.ToArray()` + +Do you mean y = y_data.ToArray(of double)() +it doesn't work at all...... + +### Comment 3 by @Jiuyong (2020-03-18T05:45:07Z) + +VB.Net don't support Of **unmanaged** +目前看到的情况是,VB.Net 不支持 C# 7.3 新的 **unmanaged** 泛型约束。 +如果想要使用,可能需要变通一下了。 + +### Comment 4 by @pepure (2020-03-18T06:57:27Z) + +> > `x_data.ToArray()` +> +> Do you mean y = y_data.ToArray(of double)() +> it doesn't work at all...... + +I offer a lower level solution:) +-------------------------------------------------------- +Dim x_data, y_data As NDArray +x_data = np.linspace(0, 100, 500) +y_data = x_data * 2 + np.random.randn(500) + +Dim y(y_data.size - 1) As Double +For i = 0 To y_data.size - 1 + y(i) = y_data(i) +Next i +-------------------------------------------------------- +Debugging results: +![image](https://user-images.githubusercontent.com/53322892/76933706-b1c04e80-6928-11ea-9825-aa453c80421b.png) +Please confirm if it can solve your problem。 + + +### Comment 5 by @Sullivanecidi (2020-03-18T07:23:03Z) + +> VB.Net don't support Of **unmanaged** +> 目前看到的情况是,VB.Net 不支持 C# 7.3 新的 **unmanaged** 泛型约束。 +> 如果想要使用,可能需要变通一下了。 + +那就可能是按照楼上这位了,一个一个取出来。谢谢你了~ + +### Comment 6 by @Sullivanecidi (2020-03-18T07:25:10Z) + +Thanks very much! this is the alternative way, since VB.net don't support the unmanaged constraint. + + +### Comment 7 by @Sullivanecidi (2020-03-18T08:38:54Z) + +> VB.Net don't support Of **unmanaged** +> 目前看到的情况是,VB.Net 不支持 C# 7.3 新的 **unmanaged** 泛型约束。 +> 如果想要使用,可能需要变通一下了。 + +我刚试了下c#,是可以用toArray()实现的。 + +### Comment 8 by @Jiuyong (2020-03-30T02:57:02Z) + +是的,C#肯定没问题啊。 +还有个稍微好一点的解决方案。 +就是用C#弄一个缝合项目,然后VB项目引用。 diff --git a/docs/issues/issue-0405-np.argsort-not-sorting-properly.md b/docs/issues/issue-0405-np.argsort-not-sorting-properly.md new file mode 100644 index 000000000..11784d711 --- /dev/null +++ b/docs/issues/issue-0405-np.argsort-not-sorting-properly.md @@ -0,0 +1,38 @@ +# #405: np.argsort not sorting properly + +- **URL:** https://github.com/SciSharp/NumSharp/issues/405 +- **State:** OPEN +- **Author:** @tk4218 +- **Created:** 2020-04-09T18:35:33Z +- **Updated:** 2020-04-09T21:18:51Z + +## Description + +I am attempting to call np.argsort() on the following array: +[0.700656592845917, 0.651415288448334, 0.719015657901764] dtype = Double + +The expected result should be: +[1, 0, 2] + +However, the actual result I'm getting is: +[0, 2, 1] + +This doesn't to produce a result that is sorted in any direction. I pulled in ndarray.argsort locally and changed line 31 - +from: `var data = Array;` +to: `var data = ToArray()` +(Also changing the declaration of the function to include `where T : unmanaged `) + +And this seemed to work for me. + + +## Comments + +### Comment 1 by @tk4218 (2020-04-09T21:18:51Z) + +After trying things out for awhile, it seems I can get np.argsort to work if I pass a copy of the NDArray to it. It seems like the NDArray is being manipulated while sorting, causing the sort to misbehave. + +It seems like np.argsort should be: +`nd.copy().argsort(axis)` + +rather than: +`nd.argsort(axis)` diff --git a/docs/issues/issue-0406-c-convert-image-to-ndarray.md b/docs/issues/issue-0406-c-convert-image-to-ndarray.md new file mode 100644 index 000000000..6920a82c1 --- /dev/null +++ b/docs/issues/issue-0406-c-convert-image-to-ndarray.md @@ -0,0 +1,20 @@ +# #406: C# --> convert image to NDarray + +- **URL:** https://github.com/SciSharp/NumSharp/issues/406 +- **State:** OPEN +- **Author:** @R06921096Yen +- **Created:** 2020-04-20T11:39:16Z +- **Updated:** 2020-05-22T09:06:36Z + +## Description + +Does anyone know how to convert "System.Drawing.Image" to "NDarray" in C#? +Any suggestions would be greatly appreciated. + +## Comments + +### Comment 1 by @chriss2401 (2020-05-22T09:06:35Z) + +Could this extension class help ? + +https://github.com/SciSharp/NumSharp/blob/master/src/NumSharp.Bitmap/np_.extensions.cs diff --git a/docs/issues/issue-0407-np.negative-is-not-working.md b/docs/issues/issue-0407-np.negative-is-not-working.md new file mode 100644 index 000000000..2a0d7eed7 --- /dev/null +++ b/docs/issues/issue-0407-np.negative-is-not-working.md @@ -0,0 +1,35 @@ +# #407: np.negative is not working ? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/407 +- **State:** OPEN +- **Author:** @LordTrololo +- **Created:** 2020-04-21T07:52:19Z +- **Updated:** 2020-04-21T13:56:16Z +- **Assignees:** @Nucs + +## Description + +Hi, + +I have a NDArray of Floats with dimensions {(1, 13, 13, 3, 2)} and size 1014. + +`np.negative(myArray)` + +works by converting all values to negative. However I think that [official Numpy behaviour](https://numpy.org/doc/stable/reference/generated/numpy.negative.html?highlight=negative#numpy.negative) is to convert positives to negatives and negatives to positives. + +Like so: +``` +np.negative([1.,-1.]) +array([-1., 1.]) + +``` + +What I get is: +``` +np.negative([1.,-1.]) +array([-1., -1.]) +``` +Did someone experience similar problem ? + +BTW, the solution I use to solve the problem is trivial - +`var negativeArray = myarray*(-1);` diff --git a/docs/issues/issue-0408-np.meshgrid-has-a-hidden-error-returning-wrong-results.md b/docs/issues/issue-0408-np.meshgrid-has-a-hidden-error-returning-wrong-results.md new file mode 100644 index 000000000..040825b51 --- /dev/null +++ b/docs/issues/issue-0408-np.meshgrid-has-a-hidden-error-returning-wrong-results.md @@ -0,0 +1,55 @@ +# #408: np.meshgrid() has a hidden error returning wrong results + +- **URL:** https://github.com/SciSharp/NumSharp/issues/408 +- **State:** OPEN +- **Author:** @LordTrololo +- **Created:** 2020-04-23T10:38:03Z +- **Updated:** 2021-07-15T12:26:10Z + +## Description + +There seems to be a bug in the `np.meshgrid()` function. It has something to do with the way the unmanaged memory is handeld but I havent tried to understand the details. + +Why hidden ? Well, lets say we have the following code: +``` +var meshAB = np.meshgrid(np.arange(3), np.arange(3)); +``` + +`meshAB.Item1` seems to be ok, but `meshAB.Item2` hides a nasty problem. +Strangely enough, when one makes a clone of Item2, the cloned values are OK! This is also my quick fix for now. + +So to summary, the code below: +``` +var meshAB = np.meshgrid(np.arange(3), np.arange(3)); +Console.WriteLine("meshAB.Item1.flatten(): " + meshAB.Item1.flatten().ToString()); +Console.WriteLine("meshAB.Item2.flatten(): " + meshAB.Item2.flatten().ToString()); + +NDArray a = meshAB.Item1.Clone(); +NDArray b = meshAB.Item2.Clone(); + +Console.WriteLine("a.flatten(): " +a.flatten().ToString()); +Console.WriteLine("b.flatten(): " +b.flatten().ToString()); +``` + +Will output: +``` +meshAB.Item1.flatten(): [0, 1, 2, 0, 1, 2, 0, 1, 2] +meshAB.Item2.flatten(): [0, 1, 2, 0, 1, 2, 0, 1, 2] <-------WRONG +a.flatten(): [0, 1, 2, 0, 1, 2, 0, 1, 2] +b.flatten(): [0, 0, 0, 1, 1, 1, 2, 2, 2] <------ CORRECT +``` +The bug is nasty because in inspector we also see ok values which is not true. Here is an image: +![image](https://user-images.githubusercontent.com/61494668/80100219-f6ff1e00-856f-11ea-8da8-91caf7e164af.png) + + +## Comments + +### Comment 1 by @ArthHil (2021-07-15T10:48:05Z) + +Same problem, but clone didnt resolve the problem + +### Comment 2 by @Oceania2018 (2021-07-15T12:24:32Z) + +I tested it in [tf.net preview](https://github.com/SciSharp/TensorFlow.NET/blob/16ff7a3dafd283b07d91f61a88e0743a3c47c9fc/test/TensorFlowNET.UnitTest/Numpy/Array.Creation.Test.cs#L69). It's working good. +![image](https://user-images.githubusercontent.com/1705364/125787200-59813f3a-616e-4b44-9f1a-435fc17fe3b2.png) +New version implemented the NumPy API in [TensorFlow NumPy](https://www.tensorflow.org/guide/tf_numpy) diff --git a/docs/issues/issue-0410-np.save-fails-with-indexoutofrangeexception-for-jagged-arrays.md b/docs/issues/issue-0410-np.save-fails-with-indexoutofrangeexception-for-jagged-arrays.md new file mode 100644 index 000000000..5ff6d3d8a --- /dev/null +++ b/docs/issues/issue-0410-np.save-fails-with-indexoutofrangeexception-for-jagged-arrays.md @@ -0,0 +1,40 @@ +# #410: np.save fails with IndexOutOfRangeException for jagged arrays + +- **URL:** https://github.com/SciSharp/NumSharp/issues/410 +- **State:** OPEN +- **Author:** @Jmerk523 +- **Created:** 2020-05-08T01:00:52Z +- **Updated:** 2020-05-08T01:00:52Z + +## Description + +Hi, +I am seeing this exception when trying to save a jagged array to a stream: + +Index was outside the bounds of the array. + at NumSharp.np.d__109`1.MoveNext() + at NumSharp.np.writeValueJagged(BinaryWriter reader, Array matrix, Int32 bytes, Int32[] shape) + at NumSharp.np.Save(Array array, Stream stream) + +I haven't pinpointed the root cause specifically, but it seems that there is some issue with jagged arrays when the outer array has rank 1: + +``` + static IEnumerable Enumerate(Array a, int[] dimensions, int pos) + { + if (pos == dimensions.Length - 1) + { + for (int i = 0; i < dimensions[pos]; i++) + yield return (T)a.GetValue(i); + } + else + { + for (int i = 0; i < dimensions[pos]; i++) + foreach (var subArray in Enumerate(a.GetValue(i) as Array, dimensions, pos + 1)) + yield return subArray; + } + } +``` +When pos == 0 and dimensions[] has length 0, the else will execute and pos(0) will be out of bounds. +dimensions[] has length 0 from the caller, which passes: +` int[] first = shape.Take(shape.Length - 1).ToArray();` +so first[] will be an empty array when shape has length 1 (as in, the single dimension of the 1 dimensional outer jagged array). diff --git a/docs/issues/issue-0411-pyobject-to-ndarray.md b/docs/issues/issue-0411-pyobject-to-ndarray.md new file mode 100644 index 000000000..3cb07567a --- /dev/null +++ b/docs/issues/issue-0411-pyobject-to-ndarray.md @@ -0,0 +1,16 @@ +# #411: PyObject to NDArray + +- **URL:** https://github.com/SciSharp/NumSharp/issues/411 +- **State:** OPEN +- **Author:** @aaronavi +- **Created:** 2020-05-20T11:24:41Z +- **Updated:** 2020-05-20T11:24:41Z + +## Description + +Hi +I have a PyObject which is of a PyDict type, It contains an NDArray, i need to convert it to actual NDArray. + +How can i do that? + +Thanks diff --git a/docs/issues/issue-0412-the-type-ndarray-exists-in-both-numsharp.core-version-0.20.5.0-and-numsh.md b/docs/issues/issue-0412-the-type-ndarray-exists-in-both-numsharp.core-version-0.20.5.0-and-numsh.md new file mode 100644 index 000000000..39d2c61f5 --- /dev/null +++ b/docs/issues/issue-0412-the-type-ndarray-exists-in-both-numsharp.core-version-0.20.5.0-and-numsh.md @@ -0,0 +1,18 @@ +# #412: The type 'NDArray' exists in both 'NumSharp.Core, Version=0.20.5.0, ' and 'NumSharp.Lite, Version=0.1.7.0, + +- **URL:** https://github.com/SciSharp/NumSharp/issues/412 +- **State:** OPEN +- **Author:** @sportbilly21 +- **Created:** 2020-05-27T11:43:26Z +- **Updated:** 2020-05-27T11:43:26Z + +## Description + +Hi +I am trying to use +Tensorflow.NET= 0.15.1 +Numsharp = 0.20.5 +SharpCV = 0.5.0 +and I getting the error +The type 'NDArray' exists in both 'NumSharp.Core, Version=0.20.5.0, ' and 'NumSharp.Lite, Version=0.1.7.0, +Any thoughts? diff --git a/docs/issues/issue-0413-ndarray-split.md b/docs/issues/issue-0413-ndarray-split.md new file mode 100644 index 000000000..dc8c46fb1 --- /dev/null +++ b/docs/issues/issue-0413-ndarray-split.md @@ -0,0 +1,34 @@ +# #413: NDArray Split + +- **URL:** https://github.com/SciSharp/NumSharp/issues/413 +- **State:** OPEN +- **Author:** @lqdev +- **Created:** 2020-06-01T15:09:12Z +- **Updated:** 2020-06-01T15:09:12Z + +## Description + +Given the following Python code: + +```python +a = [1, 2, 3, 99, 99, 3, 2, 1] +a1, a2, a3 = np.split(a, [3, 5]) +print(a1, a2, a3) +``` + +Translated to F#: + +```fsharp +let a = [|1;2;3;99;99;3;2;1|] +let a1,a2,a3 = np.split(a,[|3,5|]) +``` + +When trying to call `split`, I get the following error + +```text +typecheck error The field, constructor or member 'split' is not defined +``` + +Calling `split` on its own without any input arguments, also returns the same error. + +Is `split` implemented in NumSharp? diff --git a/docs/issues/issue-0414-implementation-of-np.delete-working.md b/docs/issues/issue-0414-implementation-of-np.delete-working.md new file mode 100644 index 000000000..eb9d61ae6 --- /dev/null +++ b/docs/issues/issue-0414-implementation-of-np.delete-working.md @@ -0,0 +1,16 @@ +# #414: Implementation of np.delete working? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/414 +- **State:** OPEN +- **Author:** @simonbuehler +- **Created:** 2020-06-16T21:50:47Z +- **Updated:** 2020-06-16T21:50:47Z + +## Description + +hi, + +currently the sourcode just returns null, shouldn't the commented code work? + + +https://github.com/SciSharp/NumSharp/blob/843309e7e873bfb0bec2d6e56b3dcba4b9e723e0/src/NumSharp.Core/Manipulation/NdArray.delete.cs#L9 diff --git a/docs/issues/issue-0415-boolean-indexing-and-np.where.md b/docs/issues/issue-0415-boolean-indexing-and-np.where.md new file mode 100644 index 000000000..51e74df5d --- /dev/null +++ b/docs/issues/issue-0415-boolean-indexing-and-np.where.md @@ -0,0 +1,18 @@ +# #415: Boolean indexing and np.where + +- **URL:** https://github.com/SciSharp/NumSharp/issues/415 +- **State:** OPEN +- **Author:** @joshmyersdean +- **Created:** 2020-06-22T15:48:40Z +- **Updated:** 2020-07-08T17:42:28Z + +## Description + +Are there any plans to implement these? Or are they already in place and I am just not getting the syntax right? Thank you! + +## Comments + +### Comment 1 by @SanftMonster (2020-07-08T17:42:28Z) + +I am also finding this...I found that nd[true] works,however not for a variable value,for example nd[nd>0]. +Could you please tell me if you have found a solution?thank you very much! diff --git a/docs/issues/issue-0416-how-to-make-numsharp.ndarray-from-numpy.ndarray.md b/docs/issues/issue-0416-how-to-make-numsharp.ndarray-from-numpy.ndarray.md new file mode 100644 index 000000000..0ef05784f --- /dev/null +++ b/docs/issues/issue-0416-how-to-make-numsharp.ndarray-from-numpy.ndarray.md @@ -0,0 +1,28 @@ +# #416: how to make NumSharp.NDArray from Numpy.NDarray? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/416 +- **State:** OPEN +- **Author:** @djagatiya +- **Created:** 2020-07-06T13:45:49Z +- **Updated:** 2020-07-07T05:43:56Z + +## Description + +_No description provided._ + +## Comments + +### Comment 1 by @djagatiya (2020-07-07T05:43:56Z) + +I found a solution, but is this the right way to do it? + +``` +Numpy.NDarray numpyArray = Numpy.np.random.randn(224,224,3); +byte[] v = numpyArray.GetData(); +fixed (byte* packet = v) +{ + var block = new UnmanagedMemoryBlock(packet, v.Length); + var storage = new UnmanagedStorage(new ArraySlice(block), numpyArray.shape.Dimensions); + NumSharp.NDArray numSharpArray = new NumSharp.NDArray(storage); +} +``` diff --git a/docs/issues/issue-0418-help-me.md b/docs/issues/issue-0418-help-me.md new file mode 100644 index 000000000..e866765f1 --- /dev/null +++ b/docs/issues/issue-0418-help-me.md @@ -0,0 +1,26 @@ +# #418: help me + +- **URL:** https://github.com/SciSharp/NumSharp/issues/418 +- **State:** OPEN +- **Author:** @mak27arr +- **Created:** 2020-08-01T17:11:03Z +- **Updated:** 2020-08-02T00:52:38Z +- **Assignees:** @Nucs + +## Description + +what im do wrong np.meshgrid return only one NDArray, second always null + +(scales, ratios) = np.meshgrid(np.array(scales), np.array(ratios)); + scales = scales.flatten(); + ratios = ratios.flatten(); + // Enumerate heights and widths from scales and ratios + var heights = scales / np.sqrt(ratios); + var widths = scales * np.sqrt(ratios); + //Enumerate shifts in feature space + var shifts_y = np.arange(0, shape[0], anchor_stride) * feature_stride; + var shifts_x = np.arange(0, shape[1], anchor_stride) * feature_stride; + (shifts_x, shifts_y) = np.meshgrid(shifts_x, shifts_y); + // Enumerate combinations of shifts, widths, and heights + var (box_widths, box_centers_x) = np.meshgrid(widths, shifts_x); + var (box_heights, box_centers_y) = np.meshgrid(heights, shifts_y); diff --git a/docs/issues/issue-0419-np.meshgrid-error.md b/docs/issues/issue-0419-np.meshgrid-error.md new file mode 100644 index 000000000..06a4abc30 --- /dev/null +++ b/docs/issues/issue-0419-np.meshgrid-error.md @@ -0,0 +1,11 @@ +# #419: np.meshgrid error + +- **URL:** https://github.com/SciSharp/NumSharp/issues/419 +- **State:** OPEN +- **Author:** @mak27arr +- **Created:** 2020-08-04T09:50:23Z +- **Updated:** 2020-08-04T09:50:23Z + +## Description + +np.meshgrid return only first item second item break program with code 3221225477 diff --git a/docs/issues/issue-0421-performance.md b/docs/issues/issue-0421-performance.md new file mode 100644 index 000000000..38180410d --- /dev/null +++ b/docs/issues/issue-0421-performance.md @@ -0,0 +1,86 @@ +# #421: Performance + +- **URL:** https://github.com/SciSharp/NumSharp/issues/421 +- **State:** OPEN +- **Author:** @mishun +- **Created:** 2020-08-16T01:11:02Z +- **Updated:** 2020-08-17T03:15:52Z + +## Description + +Hi! +I just tried to do some basic computational geometry with NumSharp and compare it with naive implementation like that: +```F# +open System +open System.Diagnostics +open System.Numerics +open NumSharp + +[] +type Line2f = { + Normal : Vector2 + Offset : single +} + +let residualsNaive (points : ReadOnlySpan, line : Line2f) = + let residuals = Array.create points.Length 0.0f + for i in 0 .. points.Length - 1 do + residuals.[i] <- abs (Vector2.Dot(line.Normal, points.[i]) + line.Offset) + residuals + + +let residuals (points : NDArray, line : Line2f) = + let l = np.array(line.Normal.X, line.Normal.Y) + let signed = np.dot(&points, &l) + line.Offset + np.abs &signed + + +[] +let main argv = + let n = 10000000 + + let rand = Random () + let points = Array.init n (fun _ -> Vector2 (single (rand.NextDouble ()), single (rand.NextDouble ()))) + let line = + let a = Math.PI * rand.NextDouble () + { Normal = Vector2 (single (cos a), single (sin a)) + Offset = single (rand.NextDouble ()) + } + + let swNaive = Stopwatch.StartNew () + let res1 = residualsNaive (ReadOnlySpan points, line) + swNaive.Stop () + + let pointsArray = np.array([| for p in points do yield p.X ; yield p.Y |]).reshape(n, 2) + + let swNumSharp = Stopwatch.StartNew () + let res2 = residuals (pointsArray, line) + swNumSharp.Stop () + + printfn "Naive: %A\nNumSharp: %A\n\n" swNaive.Elapsed swNumSharp.Elapsed + printfn "Result:\n%A vs %A" (Array.sum res1) (np.sum (&res2)) + + 0 +``` +and got: +``` +$ dotnet run -c release +Naive: 00:00:00.0762210 +NumSharp: 00:00:09.7929785 +``` +Maybe I'm doing something very-very wrong, but 2-something orders of magnitude difference looks a bit too much even if there are just managed loops inside NumSharp's functions. + +Tested with +``` +) +``` + +## Comments + +### Comment 1 by @Oceania2018 (2020-08-16T12:49:32Z) + +Can you try in Tensorflow.net preview3? + +### Comment 2 by @mishun (2020-08-17T03:15:52Z) + +You mean use NumSharp that is installed with Tensorflow.NET 0.20.0-preview3 nuget package? Runtime looks roughly the same except np.sum is now throwing an exception. diff --git a/docs/issues/issue-0422-index-of-element-with-a-condiction.md b/docs/issues/issue-0422-index-of-element-with-a-condiction.md new file mode 100644 index 000000000..647ca6b4f --- /dev/null +++ b/docs/issues/issue-0422-index-of-element-with-a-condiction.md @@ -0,0 +1,19 @@ +# #422: Index of element with a condiction + +- **URL:** https://github.com/SciSharp/NumSharp/issues/422 +- **State:** OPEN +- **Author:** @EnricoBeltramo +- **Created:** 2020-09-11T05:45:48Z +- **Updated:** 2020-09-11T05:45:48Z + +## Description + +I'm implementing a filter on array. In python it works well, but if I try to replicate in numsharp, doesn't: + +var threshold = 0.01 +var filter1 = XYZ1[2, Slice.All] < -threshold; +var XYZ_1_filter = XYZ1[Slice.All, filter1]; +var XYZ_2_filter = XYZ2[Slice.All, filter1]; + +In particular way, filter1 return null. +How can I do? In general, how i can find indexes that respect a particular condiction (i.e. greater than, minor than) in numsharp? diff --git a/docs/issues/issue-0423-system.notimplementedexception-somearray-np.frombuffer-bytebuffer.toa.md b/docs/issues/issue-0423-system.notimplementedexception-somearray-np.frombuffer-bytebuffer.toa.md new file mode 100644 index 000000000..4601f00a1 --- /dev/null +++ b/docs/issues/issue-0423-system.notimplementedexception-somearray-np.frombuffer-bytebuffer.toa.md @@ -0,0 +1,50 @@ +# #423: "System.NotImplementedException: '' --> someArray = np.frombuffer(byteBuffer.ToArray(), np.uint32); + +- **URL:** https://github.com/SciSharp/NumSharp/issues/423 +- **State:** OPEN +- **Author:** @mehmetcanbalci-Notrino +- **Created:** 2020-10-09T16:10:25Z +- **Updated:** 2020-10-18T22:51:51Z + +## Description + +Hello, +i got this execption "System.NotImplementedException: ''" when i use this code ; +someArray = np.frombuffer(byteBuffer.ToArray(), np.uint32); + +if I will use np.int32, it is working as expected. + +## Comments + +### Comment 1 by @ (2020-10-18T22:51:51Z) + +Hello @mehmetcanbalci-Notrino , + +You got an exception because data type "uint32" is not yet being implemented, only "int32" and "byte". + + ```c# + public static NDArray frombuffer(byte[] bytes, Type dtype) + { + + //TODO! all types + if (dtype.Name == "Int32") + { + var size = bytes.Length / InfoOf.Size; + var ints = new int[size]; + for (var index = 0; index < size; index++) + { + ints[index] = BitConverter.ToInt32(bytes, index * InfoOf.Size); + } + + return new NDArray(ints); + } + else if (dtype.Name == "Byte") + { + var size = bytes.Length / InfoOf.Size; + var ints = bytes; + return new NDArray(bytes); + } + + throw new NotImplementedException(""); + } +``` diff --git a/docs/issues/issue-0424-the-type-or-namespace-name-numsharp-could-not-be-found-are-you-missing-a-usin.md b/docs/issues/issue-0424-the-type-or-namespace-name-numsharp-could-not-be-found-are-you-missing-a-usin.md new file mode 100644 index 000000000..875ae50fb --- /dev/null +++ b/docs/issues/issue-0424-the-type-or-namespace-name-numsharp-could-not-be-found-are-you-missing-a-usin.md @@ -0,0 +1,36 @@ +# #424: The type or namespace name 'NumSharp' could not be found (are you missing a using directive or an assembly reference?) [Assembly-CSharp]csharp(CS0246) + +- **URL:** https://github.com/SciSharp/NumSharp/issues/424 +- **State:** OPEN +- **Author:** @rcffc +- **Created:** 2020-10-15T15:47:16Z +- **Updated:** 2020-10-19T21:52:32Z + +## Description + +I am using VSCode and have tried installing Numsharp using NuGet Gallery and Nuget Package Manager. + +But still I am getting this error in my Unity project: +`The type or namespace name 'NumSharp' could not be found (are you missing a using directive or an assembly reference?) [Assembly-CSharp]csharp(CS0246)` + +Any cues? + +## Comments + +### Comment 1 by @rcffc (2020-10-15T15:48:24Z) + +It is also included in Assembly-CSharp.csproj: + `` + +### Comment 2 by @ (2020-10-19T21:52:31Z) + +Hello @rcffc, + +1) Have you tried to restart VSCode? + +2) If your project is using .NET Core, the CLI tool allows you to easily install NuGet packages from VSCode. + + `dotnet add package NumSharp` + + After the command completes, look at the project file (*.csproj) to make sure the package was installed. + diff --git a/docs/issues/issue-0426-arctan2-returning-incorrect-value.md b/docs/issues/issue-0426-arctan2-returning-incorrect-value.md new file mode 100644 index 000000000..f59e442fe --- /dev/null +++ b/docs/issues/issue-0426-arctan2-returning-incorrect-value.md @@ -0,0 +1,48 @@ +# #426: arctan2() returning incorrect value + +- **URL:** https://github.com/SciSharp/NumSharp/issues/426 +- **State:** OPEN +- **Author:** @RoseberryPi +- **Created:** 2020-10-25T17:14:27Z +- **Updated:** 2020-10-29T22:22:09Z + +## Description + +So I'm using numsharp +`np.arctan2(np.array(-0.0012562886517319706), np.array(-0.7499033624114052))` + +I get the wrong value, approximately, `-4.33e-5` + +using numpy.net and C#'s Math library +`np.arctan2(np.array(-0.0012562886517319706), np.array(-0.7499033624114052))` +`Math.Atan2(-0.0012562886517319706, -0.7499033624114052)` + +I get `-3.1399`, which is the correct value.. Am I just being dumb and doing something wrong or is NumSharp not actually calculating the correct value? + +furthmore, `np.arctan2(1,1)` is 90deg according to numsharp. Should be 45. +`np.arctan2(1,-1)` is also 90deg.... + +I'm using version v0.20.5 + +## Comments + +### Comment 1 by @ (2020-10-29T22:22:09Z) + +Hello @RoseberryPi, + +You are right, looking at code I don't understand the type casting done to (byte*) instead of (double*). +There must be an explanation. + +```csharp +case NPTypeCode.Double: +{ + var out_addr = (double*)out_y.Address; + var out_addr_x = (byte*)out_x.Address; + Parallel.For(0, len, i => *(out_addr) = Converts.ToDouble(Math.Atan2(*(out_addr) + i, *(out_addr_x) + i))); + return out_y; +} +``` +I tried casting to (double*) and it returns the expected value, 3.13991738776297 + + + diff --git a/docs/issues/issue-0427-performance-on-np.matmul.md b/docs/issues/issue-0427-performance-on-np.matmul.md new file mode 100644 index 000000000..c71126533 --- /dev/null +++ b/docs/issues/issue-0427-performance-on-np.matmul.md @@ -0,0 +1,44 @@ +# #427: Performance on np.matmul + +- **URL:** https://github.com/SciSharp/NumSharp/issues/427 +- **State:** OPEN +- **Author:** @Banyc +- **Created:** 2020-10-31T12:26:03Z +- **Updated:** 2020-10-31T14:10:34Z + +## Description + +The shape of `x` is [200, 1000], of `w` is [1000, 500], and of `b` is [500] + +`b` is filled with zeros, `x` and `w` are random float64/double. + +Example code of NumSharp: + +```csharp +var out = np.matmul(x, w) + b; +``` + +... takes 3-4 seconds. + +Example code of numpy: + +```python +out = x @ w + b +``` + +... finishes immediately. + + +## Comments + +### Comment 1 by @Oceania2018 (2020-10-31T13:53:38Z) + +@Banyc Can you test it in `TensorFlow.NET` eager mode? + +### Comment 2 by @Banyc (2020-10-31T14:07:13Z) + +I use only this package to implement neural network layers from the stretch, without using other packages like `Tensorflow.NET`. + +### Comment 3 by @Oceania2018 (2020-10-31T14:10:34Z) + +It will have performance issue. Should use other more mature package. diff --git a/docs/issues/issue-0428-typo-in-ndarray.tomuliarray-method-name.md b/docs/issues/issue-0428-typo-in-ndarray.tomuliarray-method-name.md new file mode 100644 index 000000000..e3521f060 --- /dev/null +++ b/docs/issues/issue-0428-typo-in-ndarray.tomuliarray-method-name.md @@ -0,0 +1,13 @@ +# #428: Typo in NDArray.ToMuliArray method name + +- **URL:** https://github.com/SciSharp/NumSharp/issues/428 +- **State:** OPEN +- **Author:** @jpmn +- **Created:** 2020-11-11T20:09:52Z +- **Updated:** 2020-11-11T20:09:52Z + +## Description + +According to the filename `NumSharp.Core/Casting/NdArrayToMultiDimArray.cs`, I think it should be `NDArray.ToMultiArray` + +https://github.com/SciSharp/NumSharp/blob/843309e7e873bfb0bec2d6e56b3dcba4b9e723e0/src/NumSharp.Core/Casting/NdArrayToMultiDimArray.cs#L31 diff --git a/docs/issues/issue-0430-numsharp.backends.unmanaged.unmanagedmemoryblock1-fails-on-mono-on-linux.md b/docs/issues/issue-0430-numsharp.backends.unmanaged.unmanagedmemoryblock1-fails-on-mono-on-linux.md new file mode 100644 index 000000000..7c6282985 --- /dev/null +++ b/docs/issues/issue-0430-numsharp.backends.unmanaged.unmanagedmemoryblock1-fails-on-mono-on-linux.md @@ -0,0 +1,33 @@ +# #430: NumSharp.Backends.Unmanaged.UnmanagedMemoryBlock`1 fails on Mono on Linux + +- **URL:** https://github.com/SciSharp/NumSharp/issues/430 +- **State:** OPEN +- **Author:** @kgoderis +- **Created:** 2020-11-15T18:22:46Z +- **Updated:** 2020-11-15T18:22:46Z + +## Description + +Mono 5.12.0.301 +Linux 4.19.76 + +Attempting the "Hello World" Tensortflow.NET example in a docker environment running Mono yields the following error. Exactly the same code on Mono 6.12.0.93 on Mac OS X however does run flawlessly + +2020-11-15 17:53:41.843832: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN)to use the following CPU instructions in performance-critical operations: AVX2 FMA +To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags. +2020-11-15 17:53:41.873442: I tensorflow/core/platform/profile_utils/cpu_utils.cc:104] CPU Frequency: 2712000000 Hz +2020-11-15 17:53:41.874170: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x7fc874d557a0 initialized for platform Host (this does not guarantee that XLA will be used). Devices: +2020-11-15 17:53:41.874230: I tensorflow/compiler/xla/service/service.cc:176] StreamExecutor device (0): Host, Default Version +Stacktrace: + + at <0xffffffff> +* Assertion at method-to-ir.c:7352, condition `!sig->has_type_parameters' not met + + at NumSharp.Backends.Unmanaged.UnmanagedMemoryBlock`1.FromArray (byte[],bool) [0x00037] in <6d1fbec37f814ff9b52dec21dc0ebd1a>:0 + at NumSharp.Backends.Unmanaged.ArraySlice.FromArray (byte[],bool) [0x00000] in <6d1fbec37f814ff9b52dec21dc0ebd1a>:0 + at NumSharp.np.array (byte[]) [0x00000] in <6d1fbec37f814ff9b52dec21dc0ebd1a>:0 + at Tensorflow.Tensor.GetNDArray (Tensorflow.TF_DataType) [0x00041] in :0 + at Tensorflow.Tensor.numpy () [0x00007] in :0 + at Tensorflow.tensor_util.to_numpy_string (Tensorflow.Tensor) [0x00034] in :0 + at Tensorflow.Eager.EagerTensor.ToString () [0x00016] in :0 + diff --git a/docs/issues/issue-0433-ndarray-exists-in-both-numsharp.core-version-0.20.5.0-and-numsharp.lite-versio.md b/docs/issues/issue-0433-ndarray-exists-in-both-numsharp.core-version-0.20.5.0-and-numsharp.lite-versio.md new file mode 100644 index 000000000..69e6a3c11 --- /dev/null +++ b/docs/issues/issue-0433-ndarray-exists-in-both-numsharp.core-version-0.20.5.0-and-numsharp.lite-versio.md @@ -0,0 +1,58 @@ +# #433: NDArray exists in both NumSharp.Core, Version=0.20.5.0 and NumSharp.Lite, Version=0.1.9.0 + +- **URL:** https://github.com/SciSharp/NumSharp/issues/433 +- **State:** OPEN +- **Author:** @gscheck +- **Created:** 2020-12-09T16:50:56Z +- **Updated:** 2020-12-09T20:14:45Z + +## Description + +I get the following error when trying to compile an example: + +Severity Code Description Project File Line Suppression State +Error CS0433 The type 'NDArray' exists in both 'NumSharp.Core, Version=0.20.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' and 'NumSharp.Lite, Version=0.1.9.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' Tensorflow1 I:\Operations\Test Engineering\Test Eng\Software Development\AOI\Tensorflow1\Tensorflow1\Form1.cs 47 Active + + +I am only using TensorFlow.NET and NumSharp libraries. + +See screen shots below: + +![image](https://user-images.githubusercontent.com/32424716/101659915-6c363b00-39fb-11eb-8c40-e86669bd195c.png) + +![image](https://user-images.githubusercontent.com/32424716/101659977-7eb07480-39fb-11eb-8586-8ffa642f1059.png) + + + +## Comments + +### Comment 1 by @Oceania2018 (2020-12-09T17:59:09Z) + +Remove NumSharp reference, just reference TensorFlow.NET project. It will include NumSharp automatically. + +### Comment 2 by @gscheck (2020-12-09T18:29:12Z) + +If I remove the NumSharp reference, I get the following error. + +Severity Code Description Project File Line Suppression State +Error CS0246 The type or namespace name 'NDArray' could not be found (are you missing a using directive or an assembly reference?) Tensorflow1 I:\Operations\Test Engineering\Test Eng\Software Development\AOI\Tensorflow1\Tensorflow1\Form1.cs 46 Active + + +![image](https://user-images.githubusercontent.com/32424716/101671436-5891d100-3a09-11eb-8217-918b6444a1e0.png) + +![image](https://user-images.githubusercontent.com/32424716/101671376-431ca700-3a09-11eb-930c-5fb6c5e4dfa7.png) + + + + +### Comment 3 by @Oceania2018 (2020-12-09T20:14:35Z) + +Remove project reference means remove it from package. +![image](https://user-images.githubusercontent.com/1705364/101682026-ae29a600-3a28-11eb-8fad-6b9496827314.png) + +You still need: +```csharp +using Numsharp; +``` + +The easiest step is just follow this [sample project](https://github.com/SciSharp/SciSharp-Stack-Examples). diff --git a/docs/issues/issue-0434-accessviolationexception-when-selecting-indexes-using-ndarray-ndarray-and-setti.md b/docs/issues/issue-0434-accessviolationexception-when-selecting-indexes-using-ndarray-ndarray-and-setti.md new file mode 100644 index 000000000..33c411c9d --- /dev/null +++ b/docs/issues/issue-0434-accessviolationexception-when-selecting-indexes-using-ndarray-ndarray-and-setti.md @@ -0,0 +1,39 @@ +# #434: AccessViolationException when selecting indexes using ndarray[ndarray] and setting a scalar value + +- **URL:** https://github.com/SciSharp/NumSharp/issues/434 +- **State:** OPEN +- **Author:** @lijianxin520 +- **Created:** 2020-12-31T02:30:16Z +- **Updated:** 2021-04-21T04:50:55Z +- **Labels:** bug + +## Description + +Attempted to read or write protected memory. This is often an indication that other memory is corrupt. +![2020-12-30_230218](https://user-images.githubusercontent.com/47262889/103391183-013bd800-4b53-11eb-9f76-0ad77b978ae2.png) + + +## Comments + +### Comment 1 by @rikkitook (2021-04-01T12:43:13Z) + +To my experience, problems like these can be avoided if you do not access an instance of ndarray from different threads or tasks. If it is still necessary, try copying ndarray to float[], pass it to your method and then copy to new ndarray. +Cheers! + +### Comment 2 by @lijianxin520 (2021-04-13T06:43:06Z) + +I'm single-threaded, so I shouldn't have any problems with multithreading + +### Comment 3 by @Nucs (2021-04-13T11:19:50Z) + +To my understanding - `AccessViolationException` usually occurs if you somehow lost reference to your `NDArray`. +`NDArray` only frees allocated memory when `IDisposable` is triggered by the garbage collector when there are no longer any references to it. +In addition, zero-copied `NDArray`s from any operation still hold a reference to base memory which should prevent deallocation. + +Please provide here a reproducing unit test/piece of code and I'll gladly investigate. + +### Comment 4 by @lijianxin520 (2021-04-15T06:40:57Z) + +这个是我测试的时候异常的内容, +![image](https://user-images.githubusercontent.com/47262889/114825071-521cc700-9df8-11eb-9125-08b91b5971fd.png) +当我对一个数组执行批处理操作时,我看到的结果是这个问题. diff --git a/docs/issues/issue-0435-complex-number-support.md b/docs/issues/issue-0435-complex-number-support.md new file mode 100644 index 000000000..71bb858e4 --- /dev/null +++ b/docs/issues/issue-0435-complex-number-support.md @@ -0,0 +1,43 @@ +# #435: Complex number support? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/435 +- **State:** OPEN +- **Author:** @cgranade +- **Created:** 2021-01-07T22:43:31Z +- **Updated:** 2023-08-19T14:29:33Z + +## Description + +When attempting to create a new `NDArray` of complex numbers, I get an exception in the `Allocate` method at https://github.com/SciSharp/NumSharp/blob/00d8700b00e815f321238536e0d6b4dbc9af8d6a/src/NumSharp.Core/Backends/Unmanaged/ArraySlice.cs#L387: + +![image](https://user-images.githubusercontent.com/31516/103953204-89435400-50f6-11eb-9ec7-13e522da1445.png) + +Are complex numbers supported as the dtype of `NDArray` objects, and if so, how do I allocate them? Thanks for the help, and for the awesome project! + +## Comments + +### Comment 1 by @dcuccia (2021-06-29T00:48:34Z) + ++1. Just got to the end of a port, and realized Complex is not supported. Are there plans for this? + +### Comment 2 by @LetGo (2022-03-10T06:33:07Z) + ++1. Just got to the end of a port, and realized Complex is not supported. Are there plans for this? + +### Comment 3 by @gsgou (2023-08-19T14:26:53Z) + +Any way to workaround this one? +UnmanagedStorage also doesnt support Complex. + +``` +System.NotSupportedException: Specified method is not supported. + at NumSharp.NPTypeCodeExtensions.AsType (NumSharp.NPTypeCode typeCode) [0x00097] in D:\SciSharp\NumSharp\src\NumSharp.Core\Backends\NPTypeCode.cs:144 + at NumSharp.Backends.UnmanagedStorage..ctor (NumSharp.NPTypeCode typeCode) [0x00014] in D:\SciSharp\NumSharp\src\NumSharp.Core\Backends\Unmanaged\UnmanagedStorage.cs:181 + at NumSharp.Backends.DefaultEngine.GetStorage (NumSharp.NPTypeCode typeCode) [0x00000] in D:\SciSharp\NumSharp\src\NumSharp.Core\Backends\Default\Allocation\Default.Allocation.cs:14 + at NumSharp.NDArray..ctor (NumSharp.NPTypeCode typeCode, NumSharp.TensorEngine engine) [0x0000d] in D:\SciSharp\NumSharp\src\NumSharp.Core\Backends\NDArray.cs:102 + at NumSharp.NDArray..ctor (NumSharp.NPTypeCode typeCode) [0x00000] in D:\SciSharp\NumSharp\src\NumSharp.Core\Backends\NDArray.cs:119 + at NumSharp.NDArray..ctor (NumSharp.NPTypeCode dtype, NumSharp.Shape shape, System.Boolean fillZeros) [0x00000] in D:\SciSharp\NumSharp\src\NumSharp.Core\Backends\NDArray.cs:234 + at NumSharp.np.zeros (NumSharp.Shape shape, NumSharp.NPTypeCode typeCode) [0x0000e] in D:\SciSharp\NumSharp\src\NumSharp.Core\Creation\np.zeros.cs:54 +``` + + diff --git a/docs/issues/issue-0436-np.searchsorted-error.md b/docs/issues/issue-0436-np.searchsorted-error.md new file mode 100644 index 000000000..c018b3d30 --- /dev/null +++ b/docs/issues/issue-0436-np.searchsorted-error.md @@ -0,0 +1,19 @@ +# #436: np.searchsorted error! + +- **URL:** https://github.com/SciSharp/NumSharp/issues/436 +- **State:** OPEN +- **Author:** @wangfeixing +- **Created:** 2021-01-12T09:20:40Z +- **Updated:** 2021-01-12T09:20:40Z + +## Description + +i use "np.searchsorted" function like the code below: + List list1 = new List() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + List list2 = new List() { 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1, 9.1, 10.1 }; + NDArray array1 = np.array(list1.ToArray()); + NDArray array2 = np.array(list2.ToArray()); + + int kk = np.searchsorted(array2, 3.0); + +but it reports the error "System.IndexOutOfRangeException",is this a bug? diff --git a/docs/issues/issue-0437-argmin-is-not-the-same-with-numpy.md b/docs/issues/issue-0437-argmin-is-not-the-same-with-numpy.md new file mode 100644 index 000000000..831c02519 --- /dev/null +++ b/docs/issues/issue-0437-argmin-is-not-the-same-with-numpy.md @@ -0,0 +1,13 @@ +# #437: argmin is not the same with numpy + +- **URL:** https://github.com/SciSharp/NumSharp/issues/437 +- **State:** OPEN +- **Author:** @tomachristian +- **Created:** 2021-01-30T09:08:48Z +- **Updated:** 2021-01-30T09:08:48Z + +## Description + +https://github.com/SciSharp/NumSharp/blob/00d8700b00e815f321238536e0d6b4dbc9af8d6a/src/NumSharp.Core/Statistics/NDArray.argmin.cs#L20 + +this does not look right because it returns int, also np.argmin does not seem to behave correctly (like its numpy counterpart) diff --git a/docs/issues/issue-0438-how-to-get-the-inverse-of-a-2d-matrix.md b/docs/issues/issue-0438-how-to-get-the-inverse-of-a-2d-matrix.md new file mode 100644 index 000000000..d0668ccfc --- /dev/null +++ b/docs/issues/issue-0438-how-to-get-the-inverse-of-a-2d-matrix.md @@ -0,0 +1,13 @@ +# #438: How to get the inverse of a 2D matrix? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/438 +- **State:** OPEN +- **Author:** @Mingrui-Yu +- **Created:** 2021-02-03T16:50:22Z +- **Updated:** 2021-02-03T16:50:22Z + +## Description + +How to get the inverse of a 2D matrix ?I find NumSharp/src/NumSharp.Core/LinearAlgebra/NdArray.Inv.cs just returns null. + +Thanks for your help! diff --git a/docs/issues/issue-0439-where-is-np.where-function.md b/docs/issues/issue-0439-where-is-np.where-function.md new file mode 100644 index 000000000..95bbbe821 --- /dev/null +++ b/docs/issues/issue-0439-where-is-np.where-function.md @@ -0,0 +1,14 @@ +# #439: Where is np.where() function ? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/439 +- **State:** OPEN +- **Author:** @minhduc66532 +- **Created:** 2021-02-07T05:02:40Z +- **Updated:** 2021-04-14T11:22:27Z +- **Labels:** missing feature/s + +## Description + +The title says it all: +![image](https://user-images.githubusercontent.com/66398066/107137154-30a4e800-693c-11eb-8720-d67acfd9eead.png) + diff --git a/docs/issues/issue-0440-ndarray.tobitmap-has-critical-issue-with-24bpp-vertical-images.md b/docs/issues/issue-0440-ndarray.tobitmap-has-critical-issue-with-24bpp-vertical-images.md new file mode 100644 index 000000000..c708cf67c --- /dev/null +++ b/docs/issues/issue-0440-ndarray.tobitmap-has-critical-issue-with-24bpp-vertical-images.md @@ -0,0 +1,30 @@ +# #440: NDArray.ToBitmap() has critical issue with 24bpp VERTICAL images + +- **URL:** https://github.com/SciSharp/NumSharp/issues/440 +- **State:** OPEN +- **Author:** @MiroslavKabat +- **Created:** 2021-02-09T00:57:34Z +- **Updated:** 2021-04-14T11:21:57Z +- **Labels:** bug + +## Description + + var arr = np.ones(1, 2, 1, 3).astype(NPTypeCode.Byte); + var bmp = arr.ToBitmap(); + + for (int c = 0; c < bmp.Width; c++) + { + for (int r = 0; r < bmp.Height; r++) + { + var p = bmp.GetPixel(c, r); + Console.WriteLine($"r:{r} c:{c} => ({p.R};{p.G};{p.B})"); + } + } + + // return + // r: 0 c: 0 => (1; 1; 1) + // r: 1 c: 0 => (0; 1; 1) !!! + + // instead of + // r: 0 c: 0 => (1; 1; 1) + // r: 1 c: 0 => (1; 1; 1) diff --git a/docs/issues/issue-0441-no-numpy.linalg.norm.md b/docs/issues/issue-0441-no-numpy.linalg.norm.md new file mode 100644 index 000000000..e48782887 --- /dev/null +++ b/docs/issues/issue-0441-no-numpy.linalg.norm.md @@ -0,0 +1,12 @@ +# #441: No numpy.linalg.norm() ? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/441 +- **State:** OPEN +- **Author:** @minhduc66532 +- **Created:** 2021-02-22T15:49:06Z +- **Updated:** 2021-04-14T11:20:43Z +- **Labels:** missing feature/s + +## Description + +There is no [np.linalg.norm()](https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html) function diff --git a/docs/issues/issue-0443-0.3.0-from-nuget-throwing-notsupportedexception-on-negate-function-call.md b/docs/issues/issue-0443-0.3.0-from-nuget-throwing-notsupportedexception-on-negate-function-call.md new file mode 100644 index 000000000..4f1653893 --- /dev/null +++ b/docs/issues/issue-0443-0.3.0-from-nuget-throwing-notsupportedexception-on-negate-function-call.md @@ -0,0 +1,26 @@ +# #443: 0.3.0 from NuGet throwing NotSupportedException on negate function call + +- **URL:** https://github.com/SciSharp/NumSharp/issues/443 +- **State:** OPEN +- **Author:** @gandalfh +- **Created:** 2021-03-09T03:25:55Z +- **Updated:** 2021-04-14T11:18:02Z + +## Description + +This code behaves as expected on version 0.20.5 from NuGet (https://www.nuget.org/packages/NumSharp/) + +`var ones = np.ones((10, 1));` +`ones.negate();` + +But after upgrading to 0.3.0 a NotSupportedException is thrown: + + at NumSharp.Backends.DefaultEngine.Negate(NDArray& nd) + at NumSharp.NDArray.negate() + + +## Comments + +### Comment 1 by @Nucs (2021-04-14T11:18:01Z) + +Related to #447 diff --git a/docs/issues/issue-0445-how-can-provide-output-for-np.dot.md b/docs/issues/issue-0445-how-can-provide-output-for-np.dot.md new file mode 100644 index 000000000..c69ab0217 --- /dev/null +++ b/docs/issues/issue-0445-how-can-provide-output-for-np.dot.md @@ -0,0 +1,14 @@ +# #445: How can provide output for np.dot? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/445 +- **State:** OPEN +- **Author:** @bigdimboom +- **Created:** 2021-03-24T02:49:34Z +- **Updated:** 2021-04-23T11:58:36Z +- **Labels:** missing feature/s + +## Description + +I can't find the impl. of np.dot(inputA, inutB, out preallocatedArray). + +Currently I am using np.dot(.....).copyTo(....). But I think it still allocates a tmp memory. diff --git a/docs/issues/issue-0446-unable-to-use-np.dot-due-to-specified-method-unsupported-error.md b/docs/issues/issue-0446-unable-to-use-np.dot-due-to-specified-method-unsupported-error.md new file mode 100644 index 000000000..1e0837b5d --- /dev/null +++ b/docs/issues/issue-0446-unable-to-use-np.dot-due-to-specified-method-unsupported-error.md @@ -0,0 +1,69 @@ +# #446: Unable to use np.dot due to "Specified method unsupported" error + +- **URL:** https://github.com/SciSharp/NumSharp/issues/446 +- **State:** OPEN +- **Author:** @moonlitlyra +- **Created:** 2021-03-27T17:01:11Z +- **Updated:** 2021-06-30T18:55:04Z + +## Description + +I have recently been working on a unity project involving the genetic algorithm, but have run into an error while trying to use the function np.dot(). NumSharp has been installed using the NuGet client [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity) + +The error message is as follows: + +NotSupportedException: Specified method is not supported. +NumSharp.NPTypeCodeExtensions.GetAccumulatingType (NumSharp.NPTypeCode typeCode) (at <7807b007e09c46aca587061f8867e538>:0) +NumSharp.Backends.DefaultEngine.ReduceAdd (NumSharp.NDArray& arr, System.Nullable`1[T] axis_, System.Boolean keepdims, System.Nullable`1[T] typeCode, NumSharp.NDArray out) (at <7807b007e09c46aca587061f8867e538>:0) +NumSharp.Backends.DefaultEngine.Sum (NumSharp.NDArray& nd, System.Nullable`1[T] axis, System.Nullable`1[T] typeCode, System.Boolean keepdims) (at <7807b007e09c46aca587061f8867e538>:0) +NumSharp.np.sum (NumSharp.NDArray& a, System.Int32 axis) (at <7807b007e09c46aca587061f8867e538>:0) +NumSharp.Backends.DefaultEngine.Dot (NumSharp.NDArray& left, NumSharp.NDArray& right) (at <7807b007e09c46aca587061f8867e538>:0) +NumSharp.np.dot (NumSharp.NDArray& a, NumSharp.NDArray& b) (at <7807b007e09c46aca587061f8867e538>:0) +NN.FeedForward () (at Assets/scripts/NeuralNetwork.cs:73) +Bot.FixedUpdate () (at Assets/scripts/Bot.cs:70) + +Line 73 of the FeedForward script: + +73. `NDArray activations = np.dot(layers[i].weights, layers[i].activations);` + +Other sections of relevant code: + +47. `layer.weights = np.random.rand(3, 2);` +48. `layer.activations = np.zeros(2);` + +## Comments + +### Comment 1 by @Nucs (2021-04-14T11:16:39Z) + +Related to #447 + + +### Comment 2 by @BlackholeGH (2021-06-27T10:07:04Z) + +I seem to have this same issue, in a similar context attempting to use np.dot to do a dot-product for two 1-D numpy arrays containing double values. Assuming we're not both doing something wrong, any workarounds beside calculating the dot product manually? + +> System.NotSupportedException + HResult=0x80131515 + Message=Specified method is not supported. + Source=NumSharp + StackTrace: + at NumSharp.NPTypeCodeExtensions.GetAccumulatingType(NPTypeCode typeCode) + at NumSharp.Backends.DefaultEngine.sum_elementwise(NDArray arr, Nullable`1 typeCode) + at NumSharp.Backends.DefaultEngine.ReduceAdd(NDArray& arr, Nullable`1 axis_, Boolean keepdims, Nullable`1 typeCode, NDArray out) + at NumSharp.Backends.DefaultEngine.Dot(NDArray& left, NDArray& right) + + +### Comment 3 by @moonlitlyra (2021-06-30T18:55:04Z) + +> I seem to have this same issue, in a similar context attempting to use np.dot to do a dot-product for two 1-D numpy arrays containing double values. Assuming we're not both doing something wrong, any workarounds beside calculating the dot product manually? +> +> > System.NotSupportedException +> > HResult=0x80131515 +> > Message=Specified method is not supported. +> > Source=NumSharp +> > StackTrace: +> > at NumSharp.NPTypeCodeExtensions.GetAccumulatingType(NPTypeCode typeCode) +> > at NumSharp.Backends.DefaultEngine.sum_elementwise(NDArray arr, Nullable`1 typeCode) at NumSharp.Backends.DefaultEngine.ReduceAdd(NDArray& arr, Nullable`1 axis_, Boolean keepdims, Nullable`1 typeCode, NDArray out) +> > at NumSharp.Backends.DefaultEngine.Dot(NDArray& left, NDArray& right) + +Hello, thankyou for commenting on this issue. No workarounds as far as I am aware, I ended up having to scrap part of my project sadly. Thanks anyway. diff --git a/docs/issues/issue-0447-np.sum-is-supported-on-numsharp0.20.5-but-not-on-numsharp0.30.0.md b/docs/issues/issue-0447-np.sum-is-supported-on-numsharp0.20.5-but-not-on-numsharp0.30.0.md new file mode 100644 index 000000000..a14697f0c --- /dev/null +++ b/docs/issues/issue-0447-np.sum-is-supported-on-numsharp0.20.5-but-not-on-numsharp0.30.0.md @@ -0,0 +1,88 @@ +# #447: np.sum() Is supported on numsharp0.20.5, but not on NumSharp0.30.0 + +- **URL:** https://github.com/SciSharp/NumSharp/issues/447 +- **State:** OPEN +- **Author:** @lijianxin520 +- **Created:** 2021-04-13T06:35:26Z +- **Updated:** 2024-02-26T22:17:28Z + +## Description + +np.sum() Is supported on numsharp0.20.5, but not on NumSharp0.30.0 +the exception message :"Specified method is not supported." + +## Comments + +### Comment 1 by @Nucs (2021-04-13T11:24:36Z) + +What datatype are you trying to use? + +### Comment 2 by @lijianxin520 (2021-04-14T00:57:05Z) + + double s = np.sum(w * w); +w data type is NDArray; + +### Comment 3 by @lijianxin520 (2021-04-14T01:16:44Z) + +![image](https://user-images.githubusercontent.com/47262889/114640175-1b648500-9d02-11eb-8ef5-c75dcc150941.png) + + +### Comment 4 by @Nucs (2021-04-14T11:13:10Z) + +I mean what will this output on NumSharp 20.5? +``` C# +Console.WriteLine(w.dtype); +Console.WriteLine(s.dtype); +``` + +At version 30.x+ @Oceania2018 has removed many supported DTypes which might cause `NotSupportedException` or `NotImplementedException`. Some even return null. + +### Comment 5 by @lijianxin520 (2021-04-15T01:07:57Z) + +Thank you very much for your attention, the following is the supplementary content. +![image](https://user-images.githubusercontent.com/47262889/114799357-07845600-9dca-11eb-9374-e26c77e0e102.png) + + +### Comment 6 by @bigdimboom (2021-04-18T17:14:47Z) + +same problem. Is there a walkaround for now? + + +### Comment 7 by @Nucs (2021-04-21T04:46:24Z) + +@lijianxin520 I was not able to reproduce this locally, can you or @bigdimboom provide with a simple unit test that reproduces this and I'll take a deeper look. + +### Comment 8 by @ppsdatta (2021-04-23T10:14:54Z) + +Hello, +I encountered this problem as well and here's some data to help investigate: +1. A sample code which reproduces the problem on my Mac - Visual Studio. https://github.com/ppsdatta/NpSumIssue +2. With version 0.30.0 the code fails with the not supported error. +![Error screen shot](https://user-images.githubusercontent.com/18713580/115856850-9e948200-a44a-11eb-8a6a-4a0665002cad.png) +3. With version 0.20.5 the code works without runtime exception. +![No error screen shot](https://user-images.githubusercontent.com/18713580/115856971-c4ba2200-a44a-11eb-9e65-f97dbe509dbd.png) + + + +### Comment 9 by @badjano (2021-08-19T22:31:21Z) + +having exact same error, numsharp 0.30.0 +any workaround besides you know... for? + +### Comment 10 by @gv-collibris (2021-08-25T13:23:42Z) + +same error with NumSharp 0.30.0, with `np.sum(NDArray[])` + +### Comment 11 by @yuta0306 (2021-11-10T00:23:30Z) + +I also encountered the same error. +When the value of elements of NDArray is `double`, this error certainly occur. +To change the type `double` to `float` works well in my case. + +### Comment 12 by @guozifeng91 (2024-01-10T17:34:38Z) + +same error here, sum() works for int but not for double, using version 0.30.0 + +### Comment 13 by @PavanSuta (2024-02-26T22:17:27Z) + +I am using v4.0.30319 Numsharp. Getting the same error after calling the sum function. I am passing NDArray Double datatype. diff --git a/docs/issues/issue-0448-debug.assert-...-causes-tests-to-stop-the-entire-process.md b/docs/issues/issue-0448-debug.assert-...-causes-tests-to-stop-the-entire-process.md new file mode 100644 index 000000000..a37aa41d5 --- /dev/null +++ b/docs/issues/issue-0448-debug.assert-...-causes-tests-to-stop-the-entire-process.md @@ -0,0 +1,21 @@ +# #448: Debug.Assert(...) causes tests to stop the entire process + +- **URL:** https://github.com/SciSharp/NumSharp/issues/448 +- **State:** OPEN +- **Author:** @Nucs +- **Created:** 2021-04-23T12:03:33Z +- **Updated:** 2023-02-27T19:47:40Z +- **Labels:** bug +- **Assignees:** @Nucs + +## Description + +_No description provided._ + +## Comments + +### Comment 1 by @bojake (2023-02-27T19:47:40Z) + +The problem with this test is that the values array is not being broadcast/broadened properly. The assert expects the values array to match the size of the indices array. In this test case, though, the selector ("np < 3") is choosing 2 elements out of the 6 and the result is applying "-2" (which is another bug, btw). Changing the result value to "-2.0" gets past another bug but then you see where the actual bug in the SetIndiceND method resides. Before SetIndicesND can be called the "values" must be made to match the expected buffer size through implicit broadening. + +If you give "-2" as the value then the framework thinks you are setting the "size" of the NDArray for values instead of setting an actual value array of INTs. Oops. diff --git a/docs/issues/issue-0449-isclose-is-not-implemented-and-allclose-test-is-ignored.md b/docs/issues/issue-0449-isclose-is-not-implemented-and-allclose-test-is-ignored.md new file mode 100644 index 000000000..aeed86e0b --- /dev/null +++ b/docs/issues/issue-0449-isclose-is-not-implemented-and-allclose-test-is-ignored.md @@ -0,0 +1,26 @@ +# #449: IsClose is not implemented and allclose test is ignored + +- **URL:** https://github.com/SciSharp/NumSharp/issues/449 +- **State:** OPEN +- **Author:** @koliyo +- **Created:** 2021-04-28T17:23:19Z +- **Updated:** 2021-04-29T09:56:42Z +- **Labels:** missing feature/s + +## Description + +These should probably be removed from the API if they are not properly implemented? + +```cs + [Ignore("TODO: fix this test")] + [TestMethod] + public void np_allclose_1D() +``` + +```cs + public override NDArray IsClose(NDArray a, NDArray b, double rtol = 1.0E-5, double atol = 1.0E-8, bool equal_nan = false) + { + // ... lots of commeted out code + return null; + } +``` diff --git a/docs/issues/issue-0450-is-it-possible-to-add-numpy.diag.md b/docs/issues/issue-0450-is-it-possible-to-add-numpy.diag.md new file mode 100644 index 000000000..07c1e4ad1 --- /dev/null +++ b/docs/issues/issue-0450-is-it-possible-to-add-numpy.diag.md @@ -0,0 +1,12 @@ +# #450: Is it possible to add [numpy.diag]? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/450 +- **State:** OPEN +- **Author:** @syemhusa +- **Created:** 2021-05-05T20:29:13Z +- **Updated:** 2021-05-07T17:13:12Z +- **Labels:** missing feature/s + +## Description + +_No description provided._ diff --git a/docs/issues/issue-0451-np.argmax-is-slow.md b/docs/issues/issue-0451-np.argmax-is-slow.md new file mode 100644 index 000000000..cb79fd0b1 --- /dev/null +++ b/docs/issues/issue-0451-np.argmax-is-slow.md @@ -0,0 +1,12 @@ +# #451: np.argmax is slow + +- **URL:** https://github.com/SciSharp/NumSharp/issues/451 +- **State:** OPEN +- **Author:** @feiyuhuahuo +- **Created:** 2021-06-18T01:46:57Z +- **Updated:** 2021-06-18T01:46:57Z + +## Description + +![image](https://user-images.githubusercontent.com/32631344/122493133-bb4bd100-d019-11eb-9a89-728c4465d4e0.png) +`nd` is an array with shape of (1, 13, 512, 512), the time consumption of doing argmax on it is about 500ms. That's really slow. Any way to improve it? diff --git a/docs/issues/issue-0452-missing-feature-s-numsharps-np.around-method-is-missing-decimals-parameter.md b/docs/issues/issue-0452-missing-feature-s-numsharps-np.around-method-is-missing-decimals-parameter.md new file mode 100644 index 000000000..f063674fd --- /dev/null +++ b/docs/issues/issue-0452-missing-feature-s-numsharps-np.around-method-is-missing-decimals-parameter.md @@ -0,0 +1,15 @@ +# #452: [missing feature/s] NumSharp's np.around() method is missing decimals parameter which is available in NumPy + +- **URL:** https://github.com/SciSharp/NumSharp/issues/452 +- **State:** OPEN +- **Author:** @shashi4u +- **Created:** 2021-07-19T03:40:26Z +- **Updated:** 2021-07-19T03:59:32Z + +## Description + +In NumPy user has control over on how many decimal places the floating point number can be rounded to by setting **decimal** argument in **[numpy.around()](https://numpy.org/doc/stable/reference/generated/numpy.around.html)** method. + +However, NumSharp is missing this functionality. + +It would be better if this feature is added to NumSharp's np.around() method. diff --git a/docs/issues/issue-0454-ndarray.lstqr-doesnt-work.md b/docs/issues/issue-0454-ndarray.lstqr-doesnt-work.md new file mode 100644 index 000000000..3a5dcba8e --- /dev/null +++ b/docs/issues/issue-0454-ndarray.lstqr-doesnt-work.md @@ -0,0 +1,15 @@ +# #454: NDArray.lstqr() doesn't work + +- **URL:** https://github.com/SciSharp/NumSharp/issues/454 +- **State:** OPEN +- **Author:** @yangjiandendi +- **Created:** 2021-07-24T18:39:31Z +- **Updated:** 2021-07-24T18:39:31Z + +## Description + +can you check why `NDArray.lstqr()` doesn't work? + +I wanna finish this part, in python it like `a = np.linalg.lstsq(x,y)` + +how does it work in Numsharp? diff --git a/docs/issues/issue-0455-numsharp-does-not-allow-building-with-il2cpp-via-unity.md b/docs/issues/issue-0455-numsharp-does-not-allow-building-with-il2cpp-via-unity.md new file mode 100644 index 000000000..e075394b1 --- /dev/null +++ b/docs/issues/issue-0455-numsharp-does-not-allow-building-with-il2cpp-via-unity.md @@ -0,0 +1,23 @@ +# #455: NumSharp does not allow building with IL2CPP via Unity + +- **URL:** https://github.com/SciSharp/NumSharp/issues/455 +- **State:** OPEN +- **Author:** @julia-koziel +- **Created:** 2021-07-27T09:14:17Z +- **Updated:** 2024-03-27T15:52:40Z + +## Description + +Hi, I am having trouble with building an Android app that is using a NumSharp package in Unity. + +This is the error that I keep receiving: +IL2CPP error for type 'NumSharp.LAPACKProviderType' in assembly '/UnityProject/Temp/StagingArea/assets/bin/Data/Managed/NumSharp.Core.dll' +Additional information: Value of type 'int' is too large to convert to short. + +Any help would be much appreciated! Thanks + +## Comments + +### Comment 1 by @jacob-jacob-jacob (2024-03-27T15:52:31Z) + +Were you ever able to solve this? diff --git a/docs/issues/issue-0456-silent-catastrophe-in-implicit-casting-singleton-array-to-value-type.md b/docs/issues/issue-0456-silent-catastrophe-in-implicit-casting-singleton-array-to-value-type.md new file mode 100644 index 000000000..5a5b282ea --- /dev/null +++ b/docs/issues/issue-0456-silent-catastrophe-in-implicit-casting-singleton-array-to-value-type.md @@ -0,0 +1,41 @@ +# #456: silent catastrophe in implicit casting singleton array to value type + +- **URL:** https://github.com/SciSharp/NumSharp/issues/456 +- **State:** OPEN +- **Author:** @dmacd +- **Created:** 2021-07-29T22:27:25Z +- **Updated:** 2021-07-29T22:28:14Z + +## Description + +Hi there! + +I love NumSharp and its been a key enabler of my current project (a neurofeedback platform built in Unity3d). +However I ran across some odd behavior I feel compelled to surface. Example: + +``` +var reactor_temp = +{-6.62420108914375} + +(float)reactor_temp +-4.03896783E-28 + +(int)reactor_temp +-1845493760 + +(double)reactor_temp +-6.6242010891437531 +``` + +In this example, the actual dtype of the singleton array was double, yet I'm freely (and _implicitly_) able to cast the object to other numeric types with no warning or error. This is....counterintuitive...to say the least, has cost me several hours of debugging time, and I'm frankly lucky to have even caught it at all. Fortunately, I'm only using numsharp to manipulate people's brainwaves and not fissile material just yet :) + + +(As an aside: The issue surfaced for me when I changed an operation np.sum to np.mean, which changed the output type on me, contrary to expectation) + +If the implicit conversions cant be type-guarded or converted in a sane manner, would it make more sense to just remove them? + +Once again, grateful for all hard work that went in to this library! +Daniel + + + diff --git a/docs/issues/issue-0461-np.save-incorrectly-saves-system.byte-arrays-as-signed.md b/docs/issues/issue-0461-np.save-incorrectly-saves-system.byte-arrays-as-signed.md new file mode 100644 index 000000000..cfc821907 --- /dev/null +++ b/docs/issues/issue-0461-np.save-incorrectly-saves-system.byte-arrays-as-signed.md @@ -0,0 +1,27 @@ +# #461: np.save incorrectly saves System.Byte arrays as signed + +- **URL:** https://github.com/SciSharp/NumSharp/issues/461 +- **State:** OPEN +- **Author:** @rikkitook +- **Created:** 2021-08-04T12:06:31Z +- **Updated:** 2023-01-23T09:04:19Z + +## Description + +in function GetDtypeFromType +... +if (type == typeof(Byte)) + return "|i1"; +... +i1 gets translated to signed integer +https://numpy.org/doc/stable/user/basics.types.html + +## Comments + +### Comment 1 by @Cle-O (2023-01-23T09:03:24Z) + +Bump. This issue is still existing. +I am saving a numpy array (an image) with positive values only using _np.save()_. +Positive values only are verified calling _amin()_ on the array. +After reading the file again, the array contains negative values. +Is there a workaround for this? diff --git a/docs/issues/issue-0462-how-to-use-the-repo-to-convert-some-python-code.md b/docs/issues/issue-0462-how-to-use-the-repo-to-convert-some-python-code.md new file mode 100644 index 000000000..43e607187 --- /dev/null +++ b/docs/issues/issue-0462-how-to-use-the-repo-to-convert-some-python-code.md @@ -0,0 +1,65 @@ +# #462: How to use the repo to convert some Python code? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/462 +- **State:** OPEN +- **Author:** @zydjohnHotmail +- **Created:** 2021-09-21T07:57:34Z +- **Updated:** 2021-12-01T05:58:40Z + +## Description + +Hello: +I have the following Python code to convert one RGB image to one YUV image, and use numpy to calculate an average of one column. + +`import cv2 +import numpy as np + +img_rgb = cv2.imread('C:/Images/1.PNG') +img_yuv = cv2.cvtColor(img_rgb,cv2.COLOR_BGR2YUV) +averageV = np.average(img_yuv[:,:,2]) +print(averageV); +` + +The Python code works well. Now, I want to change it to use C# code, as I have many other C# programs will need this averageV value. + +I have done the following: +1) I created one C# console project with Visual Studio 2019 (target .NET 5.0) +2) I installed necessary NUGET packages: +PM> Install-Package OpenCvSharp4 -Version 4.5.3.20210817 +PM> Install-Package NumSharp -Version 0.30.0 +3) I have the following C# code: +`using NumSharp; +using OpenCvSharp; +using System; + +namespace ConvertRGB2YUV +{ + class Program + { + public const string Image1_File = @"C:\Images\1.PNG"; + + static void Main(string[] args) + { + Mat img_rgb = Cv2.ImRead(Image1_File); + Mat img_yuv = img_rgb.CvtColor(ColorConversionCodes.RGB2YUV); + //var averageV = (img_yuv[:,:,2]); + //averageV = np.average(img_yuv[:,:, 2]) + } + } +}` + +I can run my code, and I can see the image: img_rgb and img_yuv. +But I have no idea on how to write the python corresponding statement: +averageV = np.average(img_yuv[:,:,2]) +In NumSharp, the img_yuv[…] simply doesn’t exist. +In Python, the img_yuv is treated like an array of float numbers. +How I can do this in NumSharp? +Please advise, +Thanks, + + +## Comments + +### Comment 1 by @QingtaoLi1 (2021-12-01T05:58:40Z) + +C# doesn't support this kind of indices or slices. I guess you can explore the `NumSharp.Slice` class to reach your target. diff --git a/docs/issues/issue-0464-new-api-request-to-port-np.random.triangular.md b/docs/issues/issue-0464-new-api-request-to-port-np.random.triangular.md new file mode 100644 index 000000000..e28e1a48f --- /dev/null +++ b/docs/issues/issue-0464-new-api-request-to-port-np.random.triangular.md @@ -0,0 +1,14 @@ +# #464: New API request to port np.random.triangular + +- **URL:** https://github.com/SciSharp/NumSharp/issues/464 +- **State:** OPEN +- **Author:** @ppsdatta +- **Created:** 2021-11-23T11:50:37Z +- **Updated:** 2021-11-23T11:50:37Z + +## Description + +While porting a Python code to .NET using NumSharp as replacement for numpy - I found that there is no alternative version for `np.random.triangular` api present in NumSharp. I tried to search in the documents and didn't find any reference to it. Here is the link to the documentation for the API in numpy https://numpy.org/doc/stable/reference/random/generated/numpy.random.triangular.html + +NumSharp version I am using: `0.30.0`. + diff --git a/docs/issues/issue-0465-how-could-i-transform-between-numsharp.ndarray-with-tensorflow.numpy.ndarray.md b/docs/issues/issue-0465-how-could-i-transform-between-numsharp.ndarray-with-tensorflow.numpy.ndarray.md new file mode 100644 index 000000000..c7e2426de --- /dev/null +++ b/docs/issues/issue-0465-how-could-i-transform-between-numsharp.ndarray-with-tensorflow.numpy.ndarray.md @@ -0,0 +1,18 @@ +# #465: how could I transform between NumSharp.NDArray with Tensorflow.Numpy.NDArray? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/465 +- **State:** OPEN +- **Author:** @cross-hello +- **Created:** 2021-11-24T06:38:01Z +- **Updated:** 2021-11-27T13:45:04Z + +## Description + +Both library complement the functionality of **Numpy** library. There are some lack for methods between. So I want to know the converting method. +Give me a hand~ + +## Comments + +### Comment 1 by @Oceania2018 (2021-11-27T13:45:04Z) + +What are the missing methods? diff --git a/docs/issues/issue-0466-bug-np.random.choice-raise-exception.md b/docs/issues/issue-0466-bug-np.random.choice-raise-exception.md new file mode 100644 index 000000000..986b5668f --- /dev/null +++ b/docs/issues/issue-0466-bug-np.random.choice-raise-exception.md @@ -0,0 +1,52 @@ +# #466: [Bug] np.random.choice raise Exception + +- **URL:** https://github.com/SciSharp/NumSharp/issues/466 +- **State:** OPEN +- **Author:** @QingtaoLi1 +- **Created:** 2021-11-30T07:54:33Z +- **Updated:** 2021-12-15T05:29:47Z + +## Description + +Hi, when using `np.random.choice`, I got an Exception with message "Specified method is not supported.", and the stack trace is: + +> at NumSharp.NPTypeCodeExtensions.GetAccumulatingType(NPTypeCode typeCode) +> at NumSharp.Backends.DefaultEngine.cumsum_elementwise(NDArray& arr, Nullable`1 typeCode) +> at NumSharp.Backends.DefaultEngine.ReduceCumAdd(NDArray& arr, Nullable`1 axis_, Nullable`1 typeCode) +> at NumSharp.np.cumsum(NDArray arr, Nullable`1 axis, Nullable`1 typeCode) + +## Comments + +### Comment 1 by @QingtaoLi1 (2021-11-30T07:57:42Z) + +It seems there is no UT for this function in this repo. + +### Comment 2 by @QingtaoLi1 (2021-11-30T08:00:22Z) + +Besides, another argument `replace` is not used at all in both overrides. + +### Comment 3 by @QingtaoLi1 (2021-11-30T08:28:09Z) + +I do a simple test on `np.cumsum` since it is in the stack trace: +> public static void RandomChoice() +> { +> var array = np.arange(1, 50265); +> var arrayDouble = 1.0 / array.astype(np.@double); +> var cumsum = np.cumsum(arrayDouble, typeCode: arrayDouble.typecode); // OK +> var cumsum2 = np.cumsum(arrayDouble); // raise Exception mentioned above +> } +> + +It looks like there's something wrong in the default typeCode. BTW, I'm using the latest NumSharp 0.30.0 version. + +### Comment 4 by @UCtreespring (2021-12-14T16:25:46Z) + +相同的问题,你解决了吗? + +### Comment 5 by @QingtaoLi1 (2021-12-15T02:55:05Z) + +目前发现的解决方案就是上面那样自己copy一个,加上typeCode就能跑了 + +### Comment 6 by @UCtreespring (2021-12-15T05:29:47Z) + +是我没有留意到那个“Ok”的注释,按照你的方案,我复制了官方Numsharp-master中的相关方法,添加了相关的typeCode参数,问题解决。非常感谢! diff --git a/docs/issues/issue-0467-numsharp-and-tensorflow.net-works-on-desktop-but-fails-on-cloud-web-service-.ne.md b/docs/issues/issue-0467-numsharp-and-tensorflow.net-works-on-desktop-but-fails-on-cloud-web-service-.ne.md new file mode 100644 index 000000000..e74e88299 --- /dev/null +++ b/docs/issues/issue-0467-numsharp-and-tensorflow.net-works-on-desktop-but-fails-on-cloud-web-service-.ne.md @@ -0,0 +1,39 @@ +# #467: NumSharp and Tensorflow.NET works on Desktop but fails on Cloud Web Service (.NET 5) + +- **URL:** https://github.com/SciSharp/NumSharp/issues/467 +- **State:** OPEN +- **Author:** @marsousi +- **Created:** 2021-12-06T04:56:23Z +- **Updated:** 2021-12-06T04:56:23Z + +## Description + +NumSharp and Tensorflow.NET work fine on my desktop computer. But once I publish it on the cloud service (Azure Web Service using ASP.NET Core - .NET 5), even running a simple code to define an NDArray gives the following error: + +_DllNotFoundException: Unable to load DLL 'tensorflow' or one of its dependencies: The specified module could not be found. (0x8007007E) +Tensorflow.c_api.TF_NewStatus() + +TypeInitializationException: The type initializer for 'Tensorflow.Binding' threw an exception. +Tensorflow.Binding.get_tf()_ + +The following packages are installed in Visual Studio Solution: + + +SciSharp.TensorFlow.Redist (2.3.1)
+NumSharp (0.30.0)
+NumSharp.Bitmap (0.30.0)
+SharpCV (0.10.1)
+Rensorflow.Net (0.60.5)
+Tensorflow.Keras (0.6.5)
+Google.Protobuf (3.19.1)
+Protobuf.Text (0.5.0)
+Serilog.Sinks.Console (4.0.1)
+ +and some more but I don't think they would interfere with the above packages +
+ +Besides, I tried to change the Release CPU from AnyCPU to x64, but then the cloud service fails running. + +Am I missing something? + + diff --git a/docs/issues/issue-0468-np-array.convolve-returning-null.md b/docs/issues/issue-0468-np-array.convolve-returning-null.md new file mode 100644 index 000000000..ba56aafa5 --- /dev/null +++ b/docs/issues/issue-0468-np-array.convolve-returning-null.md @@ -0,0 +1,202 @@ +# #468: np_array.convolve returning Null + +- **URL:** https://github.com/SciSharp/NumSharp/issues/468 +- **State:** OPEN +- **Author:** @dklein9500 +- **Created:** 2021-12-06T10:57:13Z +- **Updated:** 2022-06-26T22:42:49Z + +## Description + +Hi, +I am using this function to smooth some measurement data, sadly it returns null for some reason. +Here is the example code that produced the same result: + +``` +int filter_length = 5; +List data = new List() {1, 34, 3, 4, 22, 4, 24, 42, 24, 22, 4 }; // Just some random numbers for testing +var array = data.ToArray(); +NDArray np_array = new NDArray(array); +var filter = np.ones(filterLength); +NDArray filtered_array = np_array.convolve(filter, "same"); // Here null is returned +``` +There is probably a better way to construct the NDArray, but I think the code should still work. +What am I doing wrong? +Thanks in advance! + +## Comments + +### Comment 1 by @abbefus (2022-04-28T18:02:26Z) + +This is because someone put "return null" a few lines down in the code: + +``` +public NDArray convolve(NDArray rhs, string mode = "full") +{ + var lhs = this; + int nf = lhs.shape[0]; + int ng = rhs.shape[0]; + + if (ndim > 1 || rhs.ndim > 1) + throw new IncorrectShapeException(); + var retType = np._FindCommonType(lhs, rhs); + return null; +``` + +That's enough to guarantee you get null every time. Who knows if the rest of the code ever worked. + + +### Comment 2 by @guillermoe7 (2022-06-24T18:01:36Z) + +Also facing this problem. +Have any idea if this function was in working condition before? + +### Comment 3 by @abbefus (2022-06-24T19:14:29Z) + +This function works when it is rewritten, leading me to believe it did work before. Here is the function as I rewrote it -- verified against the python version. Sorry for my comments. + +``` +public static class NumpyExtensions +{ + + // NOTE: lhs must always be bigger than rhs -- + public static NDArray LinearConvolution(this NDArray lhs, NDArray rhs, ConvolveModes mode = ConvolveModes.Full) + { + if (lhs.ndim > 1 || rhs.ndim > 1) + throw new IncorrectShapeException("Both arrays must be 1-dimensional"); + + if (lhs.Shape.Size < rhs.Shape.Size) + throw new IncorrectShapeException("Right-hand side array must be smaller than left-hand side."); + + + // NOTE: + // NDArray.GetData just runs NDArray.Storage.GetData + // which returns NDArray.Storage.InternalArray == NDArray.Array + // so all three methods are practically interchangeable so why they had to make things complicated is beyond me + + ArraySlice lhsarr = lhs.GetData(); + ArraySlice rhsarr = rhs.GetData(); + + int nf = lhs.shape[0]; + int ng = rhs.shape[0]; + + + switch (mode) + { + case ConvolveModes.Full: + { + int n = nf + ng - 1; + + NDArray ret = new NDArray(Shape.Vector(n), true); + ArraySlice outArray = ret.GetData(); + + for (int idx = 0; idx < n; ++idx) + { + int jmn = (idx >= ng - 1) ? (idx - (ng - 1)) : 0; + int jmx = (idx < nf - 1) ? idx : nf - 1; + + for (int jdx = jmn; jdx <= jmx; ++jdx) + { + outArray[idx] += lhsarr[jdx] * rhsarr[idx - jdx]; + } + } + + return ret; + } + + case ConvolveModes.Valid: + { + var min_v = (nf < ng) ? lhsarr : rhsarr; + var max_v = (nf < ng) ? rhsarr : lhsarr; + + int n = Math.Max(nf, ng) - Math.Min(nf, ng) + 1; + + var ret = new NDArray(typeof(double), Shape.Vector(n), true); + ArraySlice outArray = ret.GetData(); + + for (int idx = 0; idx < n; ++idx) + { + int kdx = idx; + + for (int jdx = (min_v.Count - 1); jdx >= 0; --jdx) + { + outArray[idx] += min_v[jdx] * max_v[kdx]; + ++kdx; + } + } + + return ret; + } + + case ConvolveModes.Same: + { + // https://stackoverflow.com/questions/38194270/matlab-convolution-same-to-numpy-convolve + var npad = rhs.shape[0] - 1; + + if (npad % 2 == 1) + { + unsafe + { + npad = (int)Math.Floor(((double)npad) / 2.0); + + ArraySlice arr = ArraySlice.Allocate(npad + lhsarr.Count); + Span span = new Span(arr.VoidAddress, arr.Count); + lhsarr.CopyTo(span, npad); + var retnd = new NDArray(new UnmanagedStorage(arr, Shape.Vector(lhsarr.Count))); + return retnd.LinearConvolution(rhs, ConvolveModes.Valid); + } + } + else + { + throw new NotImplementedException("Cannot implement because NDArray.Address is protected."); + // I suppose we could extend NDArray and create a getter for Address + //{ + // unsafe + // { + // npad = npad / 2; + + // NPTypeCode retType = NPTypeCode.Double; + // NDArray puffer = new NDArray(retType, Shape.Vector(npad + lhsarr.Count), true); + // ArraySlice puffslice = puffer.Data(); // not sure this is equal to storage + // Span span = new Span(puffslice.VoidAddress, puffslice.Count); + // //lhsarr.CopyTo(puffer.Storage.AsSpan <#202>(), npad); + // lhsarr.CopyTo(span, npad); + // NDArray np1New = puffer; + + // puffer = new NDArray(retType, Shape.Vector(npad + np1New.size), true); + // int cpylen = np1New.size * sizeof(double); + // Buffer.MemoryCopy(np1New.Address, (double)puffer.Address) + npad, cpylen, cpylen); + // return puffer.convolve(rhs, "valid"); + // } + //} + } + } + default: + return lhs.LinearConvolution(rhs); + } + } +} +``` + +``` +public enum ConvolveModes +{ + Full, + Same, + Valid +} +``` + +### Comment 4 by @guillermoe7 (2022-06-26T22:42:49Z) + +Hello abbefus, + +Thanks a lot. That's quite a good code example. +Just one more bit of help. Seems my files are not up to date as there are some resources not available in my SliceAndDice install: +ArraySlice.Allocate() +ArraySlice.CopyTo() +Shape.Vector() + +Can you please show me where to get the proper version where these methods may be found? + +Thank you very much. diff --git a/docs/issues/issue-0470-numsharp0.30.0-np.random.choice-method-missing-cause-exception.md b/docs/issues/issue-0470-numsharp0.30.0-np.random.choice-method-missing-cause-exception.md new file mode 100644 index 000000000..a5ed1073e --- /dev/null +++ b/docs/issues/issue-0470-numsharp0.30.0-np.random.choice-method-missing-cause-exception.md @@ -0,0 +1,25 @@ +# #470: Numsharp0.30.0 np.random.choice() method missing cause Exception + +- **URL:** https://github.com/SciSharp/NumSharp/issues/470 +- **State:** OPEN +- **Author:** @UCtreespring +- **Created:** 2021-12-14T10:52:50Z +- **Updated:** 2021-12-14T10:52:50Z + +## Description + +System.NotSupportedException + HResult=0x80131515 + Source=NumSharp + StackTrace: + at NumSharp.NPTypeCodeExtensions.GetAccumulatingType(NPTypeCode typeCode) + at NumSharp.Backends.DefaultEngine.cumsum_elementwise(NDArray& arr, Nullable`1 typeCode) + at NumSharp.Backends.DefaultEngine.ReduceCumAdd(NDArray& arr, Nullable`1 axis_, Nullable`1 typeCode) + at NumSharp.NumPyRandom.choice(Int32 a, Shape shape, Boolean replace, Double[] probabilities) + at NumSharp.NumPyRandom.choice(NDArray arr, Shape shape, Boolean replace, Double[] probabilities) + at PolicyGradient_TF050.PolicyGradient.ChooseAction(NDArray observation, NDArray actions, NDArray actionmask) + +I‘m new to this , + +Thanks for help. + diff --git a/docs/issues/issue-0471-unhandled-exception-system.notsupportedexception-specified-method-is-not-suppo.md b/docs/issues/issue-0471-unhandled-exception-system.notsupportedexception-specified-method-is-not-suppo.md new file mode 100644 index 000000000..f2e590351 --- /dev/null +++ b/docs/issues/issue-0471-unhandled-exception-system.notsupportedexception-specified-method-is-not-suppo.md @@ -0,0 +1,17 @@ +# #471: Unhandled Exception: System.NotSupportedException: Specified method is not supported. + +- **URL:** https://github.com/SciSharp/NumSharp/issues/471 +- **State:** OPEN +- **Author:** @KonardAdams +- **Created:** 2021-12-15T16:57:20Z +- **Updated:** 2021-12-15T16:57:20Z + +## Description + +**I am getting an error at this line of code:** + `double theta3_2 = Math.Atan2(a3, d4) - Math.Atan2(K / p3, -np.sqrt(np.power(1 - (K / p3), 2)));` + +Unhandled Exception: System.NotSupportedException: Specified method is not supported. + at NumSharp.Backends.DefaultEngine.Negate(NDArray& nd) in D:\SciSharp\NumSharp\src\NumSharp.Core\Backends\Default\Math\Default.Negate.cs:line 119 + at NumSharp.NDArray.op_UnaryNegation(NDArray x) in D:\SciSharp\NumSharp\src\NumSharp.Core\Operations\Elementwise\NDArray.Primitive.cs:line 10 + at Robotics.InverseNP.Main(String[] args) in C:\Users\.......................... diff --git a/docs/issues/issue-0472-how-to-calculate-the-rank-of-a-matrix-with-numsharp.md b/docs/issues/issue-0472-how-to-calculate-the-rank-of-a-matrix-with-numsharp.md new file mode 100644 index 000000000..f0fe63a1b --- /dev/null +++ b/docs/issues/issue-0472-how-to-calculate-the-rank-of-a-matrix-with-numsharp.md @@ -0,0 +1,15 @@ +# #472: How to calculate the rank of a matrix with NumSharp? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/472 +- **State:** OPEN +- **Author:** @drtujugkhjk +- **Created:** 2021-12-20T10:42:27Z +- **Updated:** 2021-12-20T10:42:27Z + +## Description + +I have a question,but I'm new to this. +I want to compute the rank of a matrix, but could not do it when I tried the following. + np.linalg.matrix_rank(A) ; + A:NDArray +What is wrong with it? How do I calculate the rank of a matrix? diff --git a/docs/issues/issue-0473-bit-shift-and-bit-or.md b/docs/issues/issue-0473-bit-shift-and-bit-or.md new file mode 100644 index 000000000..407b3b0af --- /dev/null +++ b/docs/issues/issue-0473-bit-shift-and-bit-or.md @@ -0,0 +1,16 @@ +# #473: Bit shift and bit or + +- **URL:** https://github.com/SciSharp/NumSharp/issues/473 +- **State:** OPEN +- **Author:** @MichielMans +- **Created:** 2021-12-28T13:21:27Z +- **Updated:** 2021-12-28T13:21:27Z + +## Description + +Is it possible to bit-shift and bit-or all values in a NDArray with NumSharp, whiteout a for loop? With Numpy (python) is works as follows: + +r_shift = r<<8 +g_shift = g<<0 +rg_concat = r_shift|g_shift + diff --git a/docs/issues/issue-0475-tobitmap-fails-if-not-contiguous-because-of-broadcast-mismatch.md b/docs/issues/issue-0475-tobitmap-fails-if-not-contiguous-because-of-broadcast-mismatch.md new file mode 100644 index 000000000..1a5a1b766 --- /dev/null +++ b/docs/issues/issue-0475-tobitmap-fails-if-not-contiguous-because-of-broadcast-mismatch.md @@ -0,0 +1,12 @@ +# #475: ToBitmap fails if not contiguous because of Broadcast mismatch + +- **URL:** https://github.com/SciSharp/NumSharp/issues/475 +- **State:** OPEN +- **Author:** @ponzis +- **Created:** 2022-02-09T15:51:52Z +- **Updated:** 2022-02-09T15:54:08Z + +## Description + +When using the `public static unsafe Bitmap ToBitmap(this NDArray nd, int width, int height, PixelFormat format = PixelFormat.DontCare)` passing a NDArray of shape (1, x, y, 3) fails due to broadcast mismatch at `(LeftShape, RightShape) = DefaultEngine.Broadcast(lhs.Shape, rhs.Shape);` due to broadcasting with `(x*y*3)` and `(1, x, y, 3)` the work around is to clone the NDArray so that it is continues or change the shape of the function so that it has a correct shape. + diff --git a/docs/issues/issue-0476-numsharp.core-contains-many-debug.assert-lines.md b/docs/issues/issue-0476-numsharp.core-contains-many-debug.assert-lines.md new file mode 100644 index 000000000..d5ef63db8 --- /dev/null +++ b/docs/issues/issue-0476-numsharp.core-contains-many-debug.assert-lines.md @@ -0,0 +1,14 @@ +# #476: Numsharp.Core contains many Debug.Assert() lines + +- **URL:** https://github.com/SciSharp/NumSharp/issues/476 +- **State:** OPEN +- **Author:** @rtwalterson +- **Created:** 2022-03-30T20:56:02Z +- **Updated:** 2022-03-30T20:56:02Z + +## Description + +great work at your side. +simply don't understand the reason why. +example: file Default.NonZero.cs + diff --git a/docs/issues/issue-0477-different-result-between-numpy-and-numsharp-with-np.matmul-function.md b/docs/issues/issue-0477-different-result-between-numpy-and-numsharp-with-np.matmul-function.md new file mode 100644 index 000000000..0bb00a39a --- /dev/null +++ b/docs/issues/issue-0477-different-result-between-numpy-and-numsharp-with-np.matmul-function.md @@ -0,0 +1,43 @@ +# #477: Different Result between NumPy and NumSharp with np.matmul Function + +- **URL:** https://github.com/SciSharp/NumSharp/issues/477 +- **State:** OPEN +- **Author:** @Koyamin +- **Created:** 2022-04-05T15:29:06Z +- **Updated:** 2022-09-09T18:42:50Z + +## Description + +I am a new learner of NumSharp and now I want to calculate the matmul product of two NDArrays by using `np.matmul` function: +```Csharp +using NumSharp; + +var a = np.arange(2 * 2 * 3).reshape((2, 2, 3)); +var b = np.array(new double[] { 1, 2, 3 }); +var res = np.matmul(a, b); +``` +The value of `res` is as follow: +``` +[[6], [24]] +``` +However I have tried the same code in Python: +```Python +import numpy as np + +a = np.arange(2*2*3).reshape((2,2,3)) +b = np.array([1,2,3]) +res = np.matmul(a, b) +``` +Now the value of `res` is +``` +[[ 8 26] + [44 62]] +``` +I have no idea about it. I want to get the result in Python, what should I do? + +## Comments + +### Comment 1 by @ChengYen-Tang (2022-09-09T18:42:50Z) + +這個專案好像已經沒有在維護了,我有發現一個更完善的專案 +https://github.com/Quansight-Labs/numpy.net diff --git a/docs/issues/issue-0479-lacking-outdated-documentation.md b/docs/issues/issue-0479-lacking-outdated-documentation.md new file mode 100644 index 000000000..1d7e572e4 --- /dev/null +++ b/docs/issues/issue-0479-lacking-outdated-documentation.md @@ -0,0 +1,19 @@ +# #479: Lacking/Outdated Documentation + +- **URL:** https://github.com/SciSharp/NumSharp/issues/479 +- **State:** OPEN +- **Author:** @Tianmaru +- **Created:** 2022-08-16T00:19:13Z +- **Updated:** 2022-09-09T18:40:32Z + +## Description + +Many methods don't seem to be documented at all, or did I just fail to find the right place to look for? +Also, is this project still maintained or is it discontinued? Seems like not much happened since the release of Numpy.NET, which is a pity. + +## Comments + +### Comment 1 by @ChengYen-Tang (2022-09-09T18:40:32Z) + +I found a more complete package, maybe you can try it. +https://github.com/Quansight-Labs/numpy.net diff --git a/docs/issues/issue-0480-numsharp-equivalent-for-unravel-index.md b/docs/issues/issue-0480-numsharp-equivalent-for-unravel-index.md new file mode 100644 index 000000000..8b8fdc1c7 --- /dev/null +++ b/docs/issues/issue-0480-numsharp-equivalent-for-unravel-index.md @@ -0,0 +1,18 @@ +# #480: Numsharp equivalent for unravel_index + +- **URL:** https://github.com/SciSharp/NumSharp/issues/480 +- **State:** OPEN +- **Author:** @iainross +- **Created:** 2022-08-30T15:49:50Z +- **Updated:** 2022-09-09T18:40:19Z + +## Description + +I can't find anything that maps onto `numpy.unravel_index` - is it missing from the `NumSharp` API or am I missing something? + +## Comments + +### Comment 1 by @ChengYen-Tang (2022-09-09T18:40:19Z) + +I found a more complete package, maybe you can try it. +https://github.com/Quansight-Labs/numpy.net diff --git a/docs/issues/issue-0481-normal-disttribution-in-numsharp.md b/docs/issues/issue-0481-normal-disttribution-in-numsharp.md new file mode 100644 index 000000000..1ab1a5dbb --- /dev/null +++ b/docs/issues/issue-0481-normal-disttribution-in-numsharp.md @@ -0,0 +1,14 @@ +# #481: Normal disttribution in NumSharp + +- **URL:** https://github.com/SciSharp/NumSharp/issues/481 +- **State:** OPEN +- **Author:** @rthota90 +- **Created:** 2022-10-11T17:19:38Z +- **Updated:** 2022-10-11T17:19:38Z + +## Description + +Hi, + +I am looking for a solution to do normal distributions NORM.S.INV, NORM.S.DIST in C# .NET. +Is it possible to achieve using NumSharp? If so How? diff --git a/docs/issues/issue-0482-c-java-optaplanner.md b/docs/issues/issue-0482-c-java-optaplanner.md new file mode 100644 index 000000000..ad3ad2269 --- /dev/null +++ b/docs/issues/issue-0482-c-java-optaplanner.md @@ -0,0 +1,11 @@ +# #482: 大佬,能否用C#还原 java 的一个求解器 optaplanner + +- **URL:** https://github.com/SciSharp/NumSharp/issues/482 +- **State:** OPEN +- **Author:** @JavaScript-zt +- **Created:** 2022-12-07T08:33:17Z +- **Updated:** 2022-12-07T08:33:17Z + +## Description + +optaplanner 用于复杂的APS,目前C#生态却没有一个类似这样的工具,建议大佬用C#实现一下。谢谢 diff --git a/docs/issues/issue-0483-how-to-convert-list-ndarray-to-ndarray.md b/docs/issues/issue-0483-how-to-convert-list-ndarray-to-ndarray.md new file mode 100644 index 000000000..1ac233297 --- /dev/null +++ b/docs/issues/issue-0483-how-to-convert-list-ndarray-to-ndarray.md @@ -0,0 +1,22 @@ +# #483: How to convert List to NDArray + +- **URL:** https://github.com/SciSharp/NumSharp/issues/483 +- **State:** OPEN +- **Author:** @williamlzw +- **Created:** 2022-12-13T14:12:14Z +- **Updated:** 2022-12-13T14:13:02Z + +## Description + +str = '00000.jpg 130,83,205,108,0 130,137,154,161,1 255,137,279,160,2 125,186,177,208,3 210,186,236,208,4 285,186,311,208,5 130,237,400,292,6 230,328,555,354,7' +line = str.split() +box = np.array([np.array(list(map(int, box.split(','))))for box in line[1:]]) +print(box) + +string str = "00000.jpg 130,83,205,108,0 130,137,154,161,1 255,137,279,160,2 125,186,177,208,3 210,186,236,208,4 285,186,311,208,5 130,237,400,292,6 230,328,555,354,7"; +var line = str.Split(); +List> allList = new List>(); +var boxarr = line.Skip(1).Select(box => np.array(box.Split(',').Select(int.Parse))).ToList(); +var aa = boxarr.ToArray();//How to convert List\ to NDArray + + diff --git a/docs/issues/issue-0484-np.load-system.exception.md b/docs/issues/issue-0484-np.load-system.exception.md new file mode 100644 index 000000000..b34c7a0bb --- /dev/null +++ b/docs/issues/issue-0484-np.load-system.exception.md @@ -0,0 +1,21 @@ +# #484: np.load System.Exception + +- **URL:** https://github.com/SciSharp/NumSharp/issues/484 +- **State:** OPEN +- **Author:** @Kiord +- **Created:** 2022-12-20T13:31:58Z +- **Updated:** 2022-12-20T13:31:58Z + +## Description + +I am loading several arrays in Unity using NumSharp's `np.load`. It works well for most of them but one throws an exception : + +![image](https://user-images.githubusercontent.com/79104227/208677853-547fae45-ffaa-4106-8670-6876fd810665.png) + +In NumPy `np.load` works fine for this this array. + +[here is a link to download this specific array](https://github.com/SciSharp/NumSharp/files/10268745/array.zip). + +What is the reason behind this exception ? + +Thank you diff --git a/docs/issues/issue-0485-np.linalg.norm-not-support.md b/docs/issues/issue-0485-np.linalg.norm-not-support.md new file mode 100644 index 000000000..63887eaaa --- /dev/null +++ b/docs/issues/issue-0485-np.linalg.norm-not-support.md @@ -0,0 +1,11 @@ +# #485: np.linalg.norm not support + +- **URL:** https://github.com/SciSharp/NumSharp/issues/485 +- **State:** OPEN +- **Author:** @williamlzw +- **Created:** 2022-12-22T03:35:00Z +- **Updated:** 2022-12-22T03:35:00Z + +## Description + +np.linalg.norm not support diff --git a/docs/issues/issue-0486-slice-assign.md b/docs/issues/issue-0486-slice-assign.md new file mode 100644 index 000000000..cf8b706a6 --- /dev/null +++ b/docs/issues/issue-0486-slice-assign.md @@ -0,0 +1,29 @@ +# #486: Slice assign + +- **URL:** https://github.com/SciSharp/NumSharp/issues/486 +- **State:** OPEN +- **Author:** @burungiu +- **Created:** 2023-01-30T10:42:54Z +- **Updated:** 2023-02-28T17:38:50Z + +## Description + +Hello everyone, there is a way to do assigment as in python? +Example: +`preds[:, :, 0] = preds[:, :, 0] % heatmapWidth` + +Thank you + +## Comments + +### Comment 1 by @bojake (2023-02-28T17:38:50Z) + +There is an issue with auto-broadening of scalar operands in the framework. + +For instance: + +var foo = (array > 12f); // fails +var foo = np.full(12f, array.shape); +var result = (array > foo); // successful + +In your "heatmapWidth" operand just create an "np.full(heatmapWidth, preds.shape)" NDArray and use that as the operand. I bet that will work for you. diff --git a/docs/issues/issue-0487-linspace-to-array-as-type-float-while-other-functions-as-type-double.md b/docs/issues/issue-0487-linspace-to-array-as-type-float-while-other-functions-as-type-double.md new file mode 100644 index 000000000..684149210 --- /dev/null +++ b/docs/issues/issue-0487-linspace-to-array-as-type-float-while-other-functions-as-type-double.md @@ -0,0 +1,25 @@ +# #487: linspace to Array as type float, while other functions as type double + +- **URL:** https://github.com/SciSharp/NumSharp/issues/487 +- **State:** OPEN +- **Author:** @changjian-github +- **Created:** 2023-02-13T12:51:52Z +- **Updated:** 2023-02-13T12:52:41Z + +## Description + +``` +using NumSharp; + +class Program +{ + static void Main(string[] args) + { + double[] x = np.arange(-1, 1.1, 0.1).ToArray(); + float[] y = np.linspace(-1, 1, 21).ToArray(); + double[] z = np.random.rand(21).ToArray(); + Console.WriteLine("compile passed"); + } +} +``` +Changing float to double will result in an error. diff --git a/docs/issues/issue-0488-np.random.choice-raised-system.notsupportedexception.md b/docs/issues/issue-0488-np.random.choice-raised-system.notsupportedexception.md new file mode 100644 index 000000000..c9ee5b58a --- /dev/null +++ b/docs/issues/issue-0488-np.random.choice-raised-system.notsupportedexception.md @@ -0,0 +1,35 @@ +# #488: np.random.choice raised System.NotSupportedException + +- **URL:** https://github.com/SciSharp/NumSharp/issues/488 +- **State:** OPEN +- **Author:** @alvinfebriando +- **Created:** 2023-02-15T06:20:28Z +- **Updated:** 2023-02-27T22:57:51Z + +## Description + +error on np.random.choice, it said "Specified method isn not supported". Is my usage wrong? + +```csharp +var score = np.arange(1,6); +var p = new[] { 0.2, 0.2, 0.2, 0.2, 0.2 }; +var result = np.random.choice(score, probabilities: p); +``` + +Stack trace +``` +Unhandled exception. System.NotSupportedException: Specified method is not supported. + at NumSharp.NPTypeCodeExtensions.GetAccumulatingType(NPTypeCode typeCode) + at NumSharp.Backends.DefaultEngine.cumsum_elementwise(NDArray& arr, Nullable`1 typeCode) + at NumSharp.Backends.DefaultEngine.ReduceCumAdd(NDArray& arr, Nullable`1 axis_, Nullable`1 typeCode) + at NumSharp.np.cumsum(NDArray arr, Nullable`1 axis, Nullable`1 typeCode) + at NumSharp.NumPyRandom.choice(Int32 a, Shape shape, Boolean replace, Double[] probabilities) + at NumSharp.NumPyRandom.choice(NDArray arr, Shape shape, Boolean replace, Double[] probabilities) + at Program.
$(String[] args) +``` + +## Comments + +### Comment 1 by @bojake (2023-02-27T22:57:51Z) + +I created a test case with your exact code and ran it using my dev fork of NumSharp. No errors were produced. Are you using an out dated NumSharp? diff --git a/docs/issues/issue-0490-np.random.choice-with-replace-false-produces-duplicates.md b/docs/issues/issue-0490-np.random.choice-with-replace-false-produces-duplicates.md new file mode 100644 index 000000000..768636e4e --- /dev/null +++ b/docs/issues/issue-0490-np.random.choice-with-replace-false-produces-duplicates.md @@ -0,0 +1,13 @@ +# #490: np.random.choice with replace: false produces duplicates + +- **URL:** https://github.com/SciSharp/NumSharp/issues/490 +- **State:** OPEN +- **Author:** @GThibeault +- **Created:** 2023-03-06T00:04:52Z +- **Updated:** 2023-03-06T00:04:52Z + +## Description + +e.g. np.random.choice(71, new Shape(40), **replace: false**) is producing duplicates. + +I've tried setting the seed to provide a reproducible example, but that doesn't seem to work either. diff --git a/docs/issues/issue-0491-tobitmap-datatype-mistmatch.md b/docs/issues/issue-0491-tobitmap-datatype-mistmatch.md new file mode 100644 index 000000000..6c337994c --- /dev/null +++ b/docs/issues/issue-0491-tobitmap-datatype-mistmatch.md @@ -0,0 +1,21 @@ +# #491: ToBitmap() - datatype mistmatch + +- **URL:** https://github.com/SciSharp/NumSharp/issues/491 +- **State:** OPEN +- **Author:** @davidvct +- **Created:** 2023-03-08T09:12:45Z +- **Updated:** 2023-03-08T09:12:45Z + +## Description + +I tried to convert a numsharp array with integers to bitmap. + +``` +array_rgb_numsharp = array_rgb_numsharp.reshape(1, 300, 300, 3); +Bitmap image = array_rgb_numsharp.ToBitmap(); +``` +and encounter this error: +System.InvalidCastException: 'Unable to perform CopyTo when T does not match dtype, use non-generic overload instead.' + +How can I fix it? + diff --git a/docs/issues/issue-0492-critical-vulnerability-in-version-5.0.2-of-system.drawing.common.md b/docs/issues/issue-0492-critical-vulnerability-in-version-5.0.2-of-system.drawing.common.md new file mode 100644 index 000000000..71ef16f09 --- /dev/null +++ b/docs/issues/issue-0492-critical-vulnerability-in-version-5.0.2-of-system.drawing.common.md @@ -0,0 +1,17 @@ +# #492: critical vulnerability in version 5.0.2 of system.drawing.common + +- **URL:** https://github.com/SciSharp/NumSharp/issues/492 +- **State:** OPEN +- **Author:** @jkl-ds +- **Created:** 2023-03-17T15:26:36Z +- **Updated:** 2023-03-17T15:27:51Z + +## Description + +"When a .NET application utilizing libgdiplus on a non-Windows system accepts input, an attacker could send a specially crafted request that could result in remote code execution." + +https://github.com/dotnet/announcements/issues/176 + +Please upgrade to version 5.0.3 or higher. + +https://github.com/SciSharp/NumSharp/blob/master/src/NumSharp.Bitmap/NumSharp.Bitmap.csproj#L78 diff --git a/docs/issues/issue-0493-numsharp-array-output-in-.net-interactive-notebooks-is-misleading.md b/docs/issues/issue-0493-numsharp-array-output-in-.net-interactive-notebooks-is-misleading.md new file mode 100644 index 000000000..ef743d316 --- /dev/null +++ b/docs/issues/issue-0493-numsharp-array-output-in-.net-interactive-notebooks-is-misleading.md @@ -0,0 +1,13 @@ +# #493: Numsharp array output in .net interactive notebooks is misleading + +- **URL:** https://github.com/SciSharp/NumSharp/issues/493 +- **State:** OPEN +- **Author:** @oxygen-dioxide +- **Created:** 2023-03-23T06:47:54Z +- **Updated:** 2023-03-23T06:47:54Z + +## Description + +image + +In [.net interactive notebooks](https://github.com/dotnet/interactive), no matter how many dimensions an array has, it will be output as an 1d array. diff --git a/docs/issues/issue-0494-hello-has-scisharp-numsharp-stopped-development-and-maintenance.md b/docs/issues/issue-0494-hello-has-scisharp-numsharp-stopped-development-and-maintenance.md new file mode 100644 index 000000000..e4c6c78e0 --- /dev/null +++ b/docs/issues/issue-0494-hello-has-scisharp-numsharp-stopped-development-and-maintenance.md @@ -0,0 +1,22 @@ +# #494: Hello, has SciSharp/NumSharp stopped development and maintenance? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/494 +- **State:** OPEN +- **Author:** @sdyby2006 +- **Created:** 2023-07-03T01:24:46Z +- **Updated:** 2024-03-31T17:58:17Z + +## Description + +Hello, has SciSharp/NumSharp stopped development and maintenance? + +## Comments + +### Comment 1 by @HCareLou (2023-09-11T16:39:49Z) + +I think so,it's so sad. + +### Comment 2 by @tingspain (2024-03-31T17:58:16Z) + +Any news about this topic? + diff --git a/docs/issues/issue-0495-what-is-the-exposed-method-for-percentiles-median-using-np.md b/docs/issues/issue-0495-what-is-the-exposed-method-for-percentiles-median-using-np.md new file mode 100644 index 000000000..b7ca0f7b8 --- /dev/null +++ b/docs/issues/issue-0495-what-is-the-exposed-method-for-percentiles-median-using-np.md @@ -0,0 +1,11 @@ +# #495: What is the exposed method for percentiles & median using np? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/495 +- **State:** OPEN +- **Author:** @vikassingh281 +- **Created:** 2023-07-19T09:40:57Z +- **Updated:** 2023-07-19T09:40:57Z + +## Description + +_No description provided._ diff --git a/docs/issues/issue-0496-can-numsharp-fit-polynomial-surface-equations.md b/docs/issues/issue-0496-can-numsharp-fit-polynomial-surface-equations.md new file mode 100644 index 000000000..dc6feab57 --- /dev/null +++ b/docs/issues/issue-0496-can-numsharp-fit-polynomial-surface-equations.md @@ -0,0 +1,11 @@ +# #496: Can NumSharp fit polynomial surface equations? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/496 +- **State:** OPEN +- **Author:** @liyu3519 +- **Created:** 2023-08-10T05:59:24Z +- **Updated:** 2023-08-10T05:59:24Z + +## Description + +Can NumSharp fit polynomial surface equations? diff --git a/docs/issues/issue-0497-np.linalg.pinv-not-supported.md b/docs/issues/issue-0497-np.linalg.pinv-not-supported.md new file mode 100644 index 000000000..490e67cba --- /dev/null +++ b/docs/issues/issue-0497-np.linalg.pinv-not-supported.md @@ -0,0 +1,23 @@ +# #497: np.linalg.pinv not supported + +- **URL:** https://github.com/SciSharp/NumSharp/issues/497 +- **State:** OPEN +- **Author:** @gsgou +- **Created:** 2023-08-12T12:35:37Z +- **Updated:** 2023-10-20T17:05:22Z + +## Description + +Here is a link to what I think is the python sources for np.linalg: +https://github.com/numpy/numpy/blob/master/numpy/linalg/linalg.py + +Other implementations: +https://github.com/TheAlgorithms/C-Sharp/blob/master/Algorithms/Numeric/Pseudoinverse/PseudoInverse.cs +https://github.com/accord-net/framework/blob/master/Sources/Accord.Math/Matrix/Matrix.Linear.Generated.cs#L298 + + +## Comments + +### Comment 1 by @YariLAN (2023-10-20T17:05:22Z) + +It’s very necessary, by the way - I have a lab on it(( diff --git a/docs/issues/issue-0498-is-there-an-example-on-how-to-use-it-with-ironpython.md b/docs/issues/issue-0498-is-there-an-example-on-how-to-use-it-with-ironpython.md new file mode 100644 index 000000000..415c6f51a --- /dev/null +++ b/docs/issues/issue-0498-is-there-an-example-on-how-to-use-it-with-ironpython.md @@ -0,0 +1,27 @@ +# #498: Is there an example on how to use it with IronPython? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/498 +- **State:** OPEN +- **Author:** @william19941994 +- **Created:** 2023-08-22T01:52:28Z +- **Updated:** 2023-08-22T01:52:28Z + +## Description + +I have a big project using c# + wpf +.net framework 4. and I can upgrade to 4.8 or 4.7.2 +and a supplier send some python examples to me. +I have to add some python script to the old project. I don't want to rewrite. + +I added IronPython yesterday, but it uses hardware communication lib, I replaced with a c# object. +and then, I found that it uses numpy. +I added another small c# class. +and then, I found that it uses lots of numpy functions. +I download this project and added a wrapper class to redirect the result. +and then, this project need System.Memory.dll 4.0.1.1, while my project has 4.5.5 .... +and I changed the prj.exe.config to lower dll version. +my exe can't run now. + +I'm re-compiling this csprj now. + +Does anyone have done this before? and where can I find a whole example or blog? + diff --git a/docs/issues/issue-0499-possible-typo-tomulidimarray.md b/docs/issues/issue-0499-possible-typo-tomulidimarray.md new file mode 100644 index 000000000..cc796d5d3 --- /dev/null +++ b/docs/issues/issue-0499-possible-typo-tomulidimarray.md @@ -0,0 +1,21 @@ +# #499: Possible typo "ToMuliDimArray()" + +- **URL:** https://github.com/SciSharp/NumSharp/issues/499 +- **State:** OPEN +- **Author:** @sappho192 +- **Created:** 2023-08-30T11:52:05Z +- **Updated:** 2024-06-12T04:11:55Z + +## Description + +Hi, recently I've been using this library and stumbled upon this method. + +https://github.com/SciSharp/NumSharp/blob/1fed94d2e556a7fdfc815775db68f9c8f195298a/src/NumSharp.Core/Casting/NdArrayToMultiDimArray.cs#L31 + +Is `ToMuliDimArray()` typo of `ToMultiDimArray()`? + +## Comments + +### Comment 1 by @sappho192 (2024-06-12T04:11:55Z) + +I've found that there'd been the pull request related to this #474 by @Karthick47v2 but don't know why it has been closed. diff --git a/docs/issues/issue-0500-the-fact-that-such-an-excellent-project-has-many-unimplemented-apis-and-is-no-lo.md b/docs/issues/issue-0500-the-fact-that-such-an-excellent-project-has-many-unimplemented-apis-and-is-no-lo.md new file mode 100644 index 000000000..210dfa878 --- /dev/null +++ b/docs/issues/issue-0500-the-fact-that-such-an-excellent-project-has-many-unimplemented-apis-and-is-no-lo.md @@ -0,0 +1,11 @@ +# #500: The fact that such an excellent project has many unimplemented APIs and is no longer being maintained is regrettable. + +- **URL:** https://github.com/SciSharp/NumSharp/issues/500 +- **State:** OPEN +- **Author:** @HCareLou +- **Created:** 2023-09-11T16:41:52Z +- **Updated:** 2023-09-11T16:41:52Z + +## Description + +The fact that such an excellent project has many unimplemented APIs and is no longer being maintained is regrettable. diff --git a/docs/issues/issue-0501-memory-leak.md b/docs/issues/issue-0501-memory-leak.md new file mode 100644 index 000000000..8cc5ce190 --- /dev/null +++ b/docs/issues/issue-0501-memory-leak.md @@ -0,0 +1,65 @@ +# #501: Memory leak? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/501 +- **State:** OPEN +- **Author:** @TakuNishiumi +- **Created:** 2023-12-13T07:32:29Z +- **Updated:** 2023-12-13T07:32:29Z + +## Description + +Hi, +I tried below code and usage rate of my memory increased up to around 10GB. + +```C# +// 配列を宣言する +double[] array = new double[110]; + +// 乱数を生成する +Random rnd = new Random(); + +// 配列に乱数を代入する +// make random double array +for (int i = 0; i < array.Length; i++) +{ + array[i] = rnd.NextDouble(); +} + +// make new NDarray in loop +for (int i = 0; i < 1000000; i++) +{ + NDArray array2 = np.array(array); + // NDArray array2 = np.argmin(array); also cause same trouble +} +``` +This is also caused when this is used as function. +I modified this and add GC.Collect(). +This make the code usage rate of my memory, but the calculation time increased. + +```C# +// 配列を宣言する +double[] array = new double[110]; + +// 乱数を生成する +Random rnd = new Random(); + +// 配列に乱数を代入する +// make random double array +for (int i = 0; i < array.Length; i++) +{ + array[i] = rnd.NextDouble(); +} + +// make new NDarray in loop +for (int i = 0; i < 1000000; i++) +{ + NDArray array2 = np.array(array); + if (i % 10000 == 0) + { + GC.Collect(); + } +} +``` + +What should I do next? +Do you have any ideas? diff --git a/docs/issues/issue-0505-np.convolve-return-null-exception.md b/docs/issues/issue-0505-np.convolve-return-null-exception.md new file mode 100644 index 000000000..7500ee32d --- /dev/null +++ b/docs/issues/issue-0505-np.convolve-return-null-exception.md @@ -0,0 +1,23 @@ +# #505: `np.convolve` return null exception + +- **URL:** https://github.com/SciSharp/NumSharp/issues/505 +- **State:** OPEN +- **Author:** @behroozbc +- **Created:** 2023-12-25T14:42:33Z +- **Updated:** 2023-12-25T14:42:33Z + +## Description + +I am new to this repository, and I want to test a convolve funcation but I got `System.NullReferenceException: Object reference not set to an instance of an object.`. I am using the NumSharp version: 0.30.0 and .net sdk version: 8.0.100 my code is simple, which I wrote in a console app. +``` +using NumSharp; +var f = np.array(new int[] { 3, 3, 2, 1, 2 }); +var g = np.array(new int[] { -1, 2, 1 }); +var outp=np.convolve(f,g, "valid"); +Console.WriteLine(outp.ToString()); +``` +the full error message +``` +Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object. + at Program.
$(String[] args) in E:\repos\ConsoleApp7\ConsoleApp7\Program.cs:line 7 +``` diff --git a/docs/issues/issue-0506-cannot-create-an-ndarray-of-shorts.md b/docs/issues/issue-0506-cannot-create-an-ndarray-of-shorts.md new file mode 100644 index 000000000..97f7e486b --- /dev/null +++ b/docs/issues/issue-0506-cannot-create-an-ndarray-of-shorts.md @@ -0,0 +1,13 @@ +# #506: Cannot create an NDArray of shorts + +- **URL:** https://github.com/SciSharp/NumSharp/issues/506 +- **State:** OPEN +- **Author:** @NickBotelho +- **Created:** 2024-01-24T14:19:58Z +- **Updated:** 2024-01-24T14:19:58Z + +## Description + +When I try to initialize an NDArray with the short type, it throws a System.NotSupportedException. It seems like it only does this with shorts, every other type works fine. Heres a line of code to run to reproduce + +`var nd = new NDArray(dtype: np.int16, shape: new Shape(new []{1, 2}));` diff --git a/docs/issues/issue-0507-np.maximum-error.md b/docs/issues/issue-0507-np.maximum-error.md new file mode 100644 index 000000000..fb6534f62 --- /dev/null +++ b/docs/issues/issue-0507-np.maximum-error.md @@ -0,0 +1,188 @@ +# #507: np.maximum error + +- **URL:** https://github.com/SciSharp/NumSharp/issues/507 +- **State:** OPEN +- **Author:** @Thanatos0173 +- **Created:** 2024-02-16T18:29:17Z +- **Updated:** 2024-02-17T14:43:40Z + +## Description + +Hello, +I'm working on a Neural Network for the video game Celeste, and I'm using Numsharp to convert a code that a friend made, but he used python. +Currently, I'm calling a train function a huge number of time, and randomly, this error pops out : +``` +One or more errors occurred. +-> at System.Threading.Tasks.Task.ThrowIfExceptional() +-> at System.Threading.Tasks.Task.Wait() +-> at System.Threading.Tasks.Task.Wait() +-> at System.Threading.Tasks.Parallel.ForWorker[TLocal]() +-> at System. Threading.Tasks. Parallel.For() +-> at NumSharp. Backends. DefaultEngine.ClipNDArray() +-> at NumSharp.np.maximum() +``` +I've googled it but found nothing relevant. +I don't really know if I can do a proper reproductible exemple, but if you need more informations about this error, i would be pleased to send them. + +Here is some of the code : + +```csharp + public static void TrainCommand() + { + NeuralNetwork.NeuralNetwork.Open(); + + for (int i = 0; i < Directory.GetFiles("Mia/Saves","*",SearchOption.AllDirectories).Length/2; i++) + { + var tdimarray = np.load($"Mia/Saves/ArraySaved_{i}.npy"); // 20x20 array + var tdiminput = np.load($"Mia/Saves/InputSaved_{i}.npy"); // 56x1 array + for(int j = 0; j < 10_000; j++) + { + double[] input = (double[])(Array)tdiminput[j]; + NeuralNetwork.NeuralNetwork.Train(lr, tdimarray[j], Utils.AllArrayFromOld(input )); + } + } + } +``` + +```csharp + public class FirstLayers + { + public NDArray weights; + public NDArray biases; + public NDArray inputs; + public NDArray outputNotActivated; + public NDArray output; + public NDArray outputGradient; + public FirstLayers(NDArray weights, NDArray biases) + { + this.weights = weights; + this.biases = biases; + } + + public void Forward(NDArray inputs) + { + this.inputs = inputs; + this.outputNotActivated = np.dot(inputs, this.weights) + this.biases; //np.dot + this.output = np.maximum(0, this.outputNotActivated); + } + + public void FirstLayerBackward(NDArray inputGradient, double learningRate) + { + bool one = DerivRelu(this.outputNotActivated) == null; + inputGradient = inputGradient * DerivRelu(this.outputNotActivated); + this.outputGradient = np.dot(inputGradient, this.weights.T); + this.weights -= np.dot(this.inputs.T, inputGradient) * learningRate / inputGradient.shape[0]; + this.biases -= np.mean(inputGradient, axis: 0) * learningRate; + } + + public NDArray DerivRelu(NDArray x) + { + NDArray result = np.zeros(x.Shape); + for (int i = 0; i < x.Shape[0]; i++) + { + for (int j = 0; j < x.Shape[1]; j++) + { + result[i, j] = (x[i, j].Data()[0] > 0 ? 1 : 0); + } + } + return result; + } + + } + + public class LastLayer + { + public NDArray weights; + public NDArray biases; + public NDArray inputs; + public NDArray outputGradient; + public NDArray output; + + public LastLayer(NDArray weights, NDArray biases) + { + this.weights = weights; + this.biases = biases; + } + + public void Forward(NDArray inputs) + { + this.inputs = inputs; + NDArray outputNotActivated = np.dot(inputs, this.weights) + this.biases; + NDArray expValues = np.exp(outputNotActivated - np.max(outputNotActivated, axis: 1, keepdims: true)); + this.output = expValues / np.sum(expValues.astype(NPTypeCode.Float), axis: 1, keepdims: true); + } + + public void LastLayerBackward(NDArray yPred, NDArray yTrue, double learningRate) + { + NDArray inputGradient = yPred - yTrue; + this.outputGradient = np.dot(inputGradient, this.weights.T); + this.weights -= np.dot(this.inputs.T, inputGradient) * learningRate / inputGradient.shape[0]; + this.biases -= np.mean(inputGradient, axis: 0) * learningRate; + } + } + + private static Tuple, LastLayer> nn; + + public static void Open() + { + var weights = new List (); + var biases = new List (); + foreach(string file in Directory.GetFiles("Mia/weights")) + { + weights.Add(np.load (file)); + } + foreach (string file in Directory.GetFiles("Mia/biases")) + { + biases.Add(np.load(file)); + } + + int n = weights.Count; + + nn = new Tuple, LastLayer>( + new List(), + new LastLayer(weights[n - 1], biases[n - 1])); // I gave it a new value... We'll see. + for (int j = 0; j < n - 1; j++) + { + nn.Item1.Add(new FirstLayers(weights[j], biases[j])); + } + } + + public static void Train(double lr, NDArray allTiles, int[] keypress) + { + NDArray trueInputs = allTiles.reshape(1, 400); + NDArray labels = new NDArray(keypress); + + NDArray output = ForPropagation(trueInputs); + + + BackPropagation(output, labels, lr); + + + } + public static NDArray ForPropagation(NDArray input) + { + nn.Item1[0].Forward(input); + for (int i = 1; i < nn.Item1.Count; i++) // Adding all the values inside of FirstLayer + { + nn.Item1[i].Forward(nn.Item1[i - 1].output); + } + + nn.Item2.Forward(nn.Item1[nn.Item1.Count - 1].output); + + return nn.Item2.output; + } + + public static void BackPropagation(NDArray yPred, NDArray yTrue, double lr) + { + // Your implementation for the BackPropagation function + nn.Item2.LastLayerBackward(yPred, yTrue, lr); + nn.Item1[nn.Item1.Count - 1].FirstLayerBackward(nn.Item2.outputGradient, lr); + for (int i = nn.Item1.Count - 3; i >= 0; i--) + { + nn.Item1[i].FirstLayerBackward(nn.Item1[i + 1].outputGradient, lr); + } + } + } +} + +``` diff --git a/docs/issues/issue-0508-np.hstack-has-diffrent-effect-from-python.md b/docs/issues/issue-0508-np.hstack-has-diffrent-effect-from-python.md new file mode 100644 index 000000000..121e0a8a1 --- /dev/null +++ b/docs/issues/issue-0508-np.hstack-has-diffrent-effect-from-python.md @@ -0,0 +1,54 @@ +# #508: np.hstack has diffrent effect from python + +- **URL:** https://github.com/SciSharp/NumSharp/issues/508 +- **State:** OPEN +- **Author:** @xdqa01 +- **Created:** 2024-02-17T08:19:50Z +- **Updated:** 2024-02-18T00:28:53Z + +## Description + +Windows 10@19044.2364 +Dotnet@net8.0-windows,wpf +NumSharp@0.30.0 +OpenCvSharp4@4.9.0.20240103 +Python@3.12 + + +NumSharp np.hstack(img1,img2,img3) +```csharp + var image1 = Cv2.ImRead(FirstImagePath).NotNull().ResizeToStandardSize(); + var image2 = Cv2.ImRead(SecondImagePath).NotNull().ResizeToStandardSize(); + var image3 = Cv2.ImRead(ThirdImagePath).NotNull().ResizeToStandardSize(); + var imageArray = np.hstack(image1.ToNDArray(), image2.ToNDArray(), image3.ToNDArray()); + var image = imageArray.ToMat(); + Cv2.ImShow(WindowName, image); + FourthImageSource = image.ToBitmapSource(); +``` + +print +- img1 +- img2 +- img3 + +python np.hstack(img1,img2,img3) +```python + img1 = cv.imread("./static/fllower.jpg") + img2 = cv.imread("./static/lake.jpg") + img3 = cv.imread("./static/mountain.jpg") + img1 = cv.resize(img1, (200, 200)) + img2 = cv.resize(img2, (200, 200)) + img3 = cv.resize(img3, (200, 200)) + imgs = np.hstack([img1, img2, img3]) + cv.imshow("multi_pic", imgs) +``` + +print +img1 img2 img3 + + +And,if i use code below,it print like python np.hstack +```csharp +var imageArray = np.dstack(image1.ToNDArray(), image2.ToNDArray(), image3.ToNDArray()); +``` + diff --git a/docs/issues/issue-0509-extremely-poor-performance-on-sum-reduce.md b/docs/issues/issue-0509-extremely-poor-performance-on-sum-reduce.md new file mode 100644 index 000000000..e2893810c --- /dev/null +++ b/docs/issues/issue-0509-extremely-poor-performance-on-sum-reduce.md @@ -0,0 +1,92 @@ +# #509: Extremely poor performance on sum reduce + +- **URL:** https://github.com/SciSharp/NumSharp/issues/509 +- **State:** OPEN +- **Author:** @lucdem +- **Created:** 2024-02-19T23:32:37Z +- **Updated:** 2024-03-27T04:09:12Z + +## Description + +Creating a large 3 dimensional array and calling sum(axis: 0) takes an enormous amount of time on my machine (11s), far more than Numpy (around 75ms) or even just a simple C# naive implementation (around 200ms). + +C# Code: + +``` +using NumSharp; +using System.Diagnostics; +using System.Linq; + +namespace NumSharpTest; + +internal class Program +{ + static void Main(string[] args) + { + Console.WriteLine("NumSharp"); + NumSharp(); + Console.WriteLine("#######################"); + Console.WriteLine("Naive"); + Naive(); + } + + static void NumSharp() + { + var randArr = np.random.uniform(0, 1, [500, 500, 500]).astype(np.float32); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var linesSum = randArr.sum(axis: 0); + stopwatch.Stop(); + Console.WriteLine($"Elapsed: {stopwatch.ElapsedMilliseconds} ms"); + } + + static void Naive() + { + var random = new Random(); + var shape = new int[] { 500, 500, 500 }; + var randArr = new float[shape[0] * shape[1] * shape[2]]; + for (int i = 0; i < randArr.Length; i++) { randArr[i] = (float)random.NextDouble(); } + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var sum = new float[shape[1] * shape[2]]; + for (int i = 0; i < shape[0]; i++) + { + for (int j = 0; j < shape[1]; j++) + { + for (int k = 0; k < shape[2]; k++) + { + sum[shape[2] * j + k] += randArr[shape[1] * shape[2] * i + shape[2] * j + k]; + } + } + } + stopwatch.Stop(); + Console.WriteLine($"Elapsed: {stopwatch.ElapsedMilliseconds} ms"); + } +} + + +``` + +Python Code: + +``` +import numpy as np +from time import time + +randArr = np.random.normal(1, 0.5, (500, 500, 500)) + +start = time() +sum = randArr.sum(axis=0) +end = time() + +print(f'Elapsed: {(end - start) * 1000} ms') +``` + +## Comments + +### Comment 1 by @vkribo (2024-03-27T04:09:11Z) + +I profiled it and the problem seems to be in the method GetOffset in the Shape class. In the method there is a list called coords being created milions of times. I tried reimplementing it using a stackalloced array instead and it went from 6287 ms to 2315 ms. +Maybe someone more familliar with the code could look into it. diff --git a/docs/issues/issue-0510-how-to-save-a-nested-dictionary-with-save-npz.md b/docs/issues/issue-0510-how-to-save-a-nested-dictionary-with-save-npz.md new file mode 100644 index 000000000..10bb76d3e --- /dev/null +++ b/docs/issues/issue-0510-how-to-save-a-nested-dictionary-with-save-npz.md @@ -0,0 +1,25 @@ +# #510: How to save a nested dictionary with `save_npz` + +- **URL:** https://github.com/SciSharp/NumSharp/issues/510 +- **State:** OPEN +- **Author:** @rqx110 +- **Created:** 2024-03-07T01:20:45Z +- **Updated:** 2024-03-07T01:20:45Z + +## Description + +If i have a data struct like: +```json +{ + "port1": { + "time": ["xxxx", "yyyy"], + "data": [0.0, 0.0] + }, + "port2": { + "time": ["xxxx", "yyyy"], + "data": [0.0, 0.0] + }, +} +``` +how to save it with `save_npz`? +can you give same sample codes? Thanks! diff --git a/docs/issues/issue-0511-does-numsharp-work-in-unity-with-the-il2cpp-backend.md b/docs/issues/issue-0511-does-numsharp-work-in-unity-with-the-il2cpp-backend.md new file mode 100644 index 000000000..041766775 --- /dev/null +++ b/docs/issues/issue-0511-does-numsharp-work-in-unity-with-the-il2cpp-backend.md @@ -0,0 +1,13 @@ +# #511: Does Numsharp work in Unity with the IL2CPP backend? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/511 +- **State:** OPEN +- **Author:** @jacob-jacob-jacob +- **Created:** 2024-03-27T15:28:35Z +- **Updated:** 2024-03-27T15:28:35Z + +## Description + +In the following thread there is some nice advice on how to set up Numsharp in unity: +https://github.com/SciSharp/NumSharp/issues/353 +However, I've been asking myself if people have tested its performance. I reccon that it should work well with the Unitys Mono backend, but will it also have relatively good performance if choosing the IL2CPP backend? diff --git a/docs/issues/issue-0512-how-to-change-to-microsoft.ml.onnxruntime.tensors.densetensor-float.md b/docs/issues/issue-0512-how-to-change-to-microsoft.ml.onnxruntime.tensors.densetensor-float.md new file mode 100644 index 000000000..fabb37bb3 --- /dev/null +++ b/docs/issues/issue-0512-how-to-change-to-microsoft.ml.onnxruntime.tensors.densetensor-float.md @@ -0,0 +1,26 @@ +# #512: how to change to Microsoft.ML.OnnxRuntime.Tensors.DenseTensor? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/512 +- **State:** OPEN +- **Author:** @BingGitCn +- **Created:** 2024-04-01T09:13:01Z +- **Updated:** 2024-04-01T09:13:01Z + +## Description + + public static void ExtractPixelsArgb(DenseTensor tensor, Span data, int pixelCount) + { + Span spanR = tensor.Buffer.Span; + Span spanG = spanR[pixelCount..]; + Span spanB = spanG[pixelCount..]; + + int sidx = 0; + for (int i = 0; i < pixelCount; i++) + { + spanR[i] = data[sidx + 2] * 0.0039215686274509803921568627451f; + spanG[i] = data[sidx + 1] * 0.0039215686274509803921568627451f; + spanB[i] = data[sidx] * 0.0039215686274509803921568627451f; + sidx += 4; + } + } +Is there a faster way than this? diff --git a/docs/issues/issue-0514-setitem-for-multiple-ids-not-working.md b/docs/issues/issue-0514-setitem-for-multiple-ids-not-working.md new file mode 100644 index 000000000..b161af37e --- /dev/null +++ b/docs/issues/issue-0514-setitem-for-multiple-ids-not-working.md @@ -0,0 +1,41 @@ +# #514: SetItem for multiple Ids not working + +- **URL:** https://github.com/SciSharp/NumSharp/issues/514 +- **State:** OPEN +- **Author:** @MaxOmlor +- **Created:** 2024-06-03T07:57:24Z +- **Updated:** 2024-06-03T07:57:24Z + +## Description + +```C# +[Test] + public void MultiIdSetItem() + { + var a = np.arange(5); + Debug.Log($"a = {a}"); + var ids = np.array(new int[] { 1, 3 }); + Debug.Log($"ids = {ids}"); + var newValues = np.array(new int[] { 10, 20 }); + Debug.Log($"newValues = {newValues}"); + + a[ids] = newValues; + Debug.Log($"after set item: a = {a}"); + + var expected = np.array(new[] { 0, 10, 2, 20, 4 }); + Assert.AreEqual(expected, a); + } +``` +Output: +``` +Expected and actual are both + Values differ at index [1] + Expected: 10 + But was: 1 +--- +a = [0, 1, 2, 3, 4] +ids = [1, 3] +newValues = [10, 20] +after set item: a = [0, 1, 2, 3, 4] +``` + diff --git a/docs/issues/issue-0515-np.tile-not-implemented.md b/docs/issues/issue-0515-np.tile-not-implemented.md new file mode 100644 index 000000000..3607cc5bf --- /dev/null +++ b/docs/issues/issue-0515-np.tile-not-implemented.md @@ -0,0 +1,11 @@ +# #515: np.tile not implemented + +- **URL:** https://github.com/SciSharp/NumSharp/issues/515 +- **State:** OPEN +- **Author:** @MaxOmlor +- **Created:** 2024-06-03T10:08:23Z +- **Updated:** 2024-06-03T10:08:23Z + +## Description + +getting message 'Cannot resolve symbol 'tile'' when i try to use np.tile diff --git a/docs/issues/issue-0516-very-helpful-work.-keep-at-it.md b/docs/issues/issue-0516-very-helpful-work.-keep-at-it.md new file mode 100644 index 000000000..4dc2e33a2 --- /dev/null +++ b/docs/issues/issue-0516-very-helpful-work.-keep-at-it.md @@ -0,0 +1,11 @@ +# #516: Very helpful work. Keep at it + +- **URL:** https://github.com/SciSharp/NumSharp/issues/516 +- **State:** OPEN +- **Author:** @duxuan11 +- **Created:** 2024-08-13T08:13:55Z +- **Updated:** 2024-08-13T08:13:55Z + +## Description + +Such a great job, hope to continue to deal with issues, come on. diff --git a/docs/issues/issue-0517-error-when-loading-a-.npy-file-containing-a-scalar-value.md b/docs/issues/issue-0517-error-when-loading-a-.npy-file-containing-a-scalar-value.md new file mode 100644 index 000000000..d3541897d --- /dev/null +++ b/docs/issues/issue-0517-error-when-loading-a-.npy-file-containing-a-scalar-value.md @@ -0,0 +1,12 @@ +# #517: Error when loading a `.npy` file containing a scalar value + +- **URL:** https://github.com/SciSharp/NumSharp/issues/517 +- **State:** OPEN +- **Author:** @thalesfm +- **Created:** 2024-09-24T17:56:29Z +- **Updated:** 2024-09-24T17:56:29Z + +## Description + +Due to an off-by-one error, `np.load` fails to parse the file's header when `shape = ()` and triggers an `ArgumentOutOfRangeException` +(Also, the function doesn't consider this possibility when calculating the size of the underlying buffer). diff --git a/docs/issues/issue-0519-bug-ndarray-filted-array-ori-array-max-prob-conf-threshold.md b/docs/issues/issue-0519-bug-ndarray-filted-array-ori-array-max-prob-conf-threshold.md new file mode 100644 index 000000000..2e0b5d739 --- /dev/null +++ b/docs/issues/issue-0519-bug-ndarray-filted-array-ori-array-max-prob-conf-threshold.md @@ -0,0 +1,18 @@ +# #519: BUG: NDArray filted_array = ori_array[max_prob > conf_threshold]; + +- **URL:** https://github.com/SciSharp/NumSharp/issues/519 +- **State:** OPEN +- **Author:** @1Zengy +- **Created:** 2024-11-11T09:09:51Z +- **Updated:** 2024-11-11T09:09:51Z + +## Description + +In the C# code, NDArray filted_array = ori_array[max_prob > conf_threshold]; +Here, `ori_array` is an `NDArray` with a shape of [1, 8400, 5], where 5 represents the five probabilities of 5 classes. `max_prob` is an `NDArray` with a shape of [1, 8400], indicating the maximum probability among the 5 classes. `conf_threshold` is a float value which represents the threshold of probability. + +I would like to delete the objects in the 8400 dimension where the maximum probability is lower than the `conf_threshold`. However, even though I have ensured that `ori_array` and `max_prob` have the same values, the code will randomly return either an exact value with a shape of [n, 5] or a null value with a shape of [0, 5]. Here is my data saved using "np.save()". +[ori_array.json](https://github.com/user-attachments/files/17699927/ori_array.json) +[max_prob.json](https://github.com/user-attachments/files/17699931/max_prob.json) + + diff --git a/docs/issues/issue-0520-cant-convert-to-vector3.md b/docs/issues/issue-0520-cant-convert-to-vector3.md new file mode 100644 index 000000000..d18e090ed --- /dev/null +++ b/docs/issues/issue-0520-cant-convert-to-vector3.md @@ -0,0 +1,21 @@ +# #520: Can't convert to Vector3 + +- **URL:** https://github.com/SciSharp/NumSharp/issues/520 +- **State:** OPEN +- **Author:** @xiaoshux +- **Created:** 2025-03-12T06:09:51Z +- **Updated:** 2025-12-16T13:45:12Z + +## Description + +In Unity, this bug trapped me for 5 days—I couldn't pass values into Vector3. I don't recommend using NumSharp! + +![Image](https://github.com/user-attachments/assets/1e1ee948-510f-4373-b02d-a8b7bc0bef4f) + +![Image](https://github.com/user-attachments/assets/35dfd893-3a91-4496-abda-9ce46aa10268) + +## Comments + +### Comment 1 by @zhuoshui-AI (2025-12-16T13:45:12Z) + +Bcausse in this a[1,1,1] return tpye is NDarray of one element.Not a scalar. try to use .getInt32() np.asscalar()to get a pure number diff --git a/docs/issues/issue-0523-multi-threading-supported.md b/docs/issues/issue-0523-multi-threading-supported.md new file mode 100644 index 000000000..82bc2baa2 --- /dev/null +++ b/docs/issues/issue-0523-multi-threading-supported.md @@ -0,0 +1,11 @@ +# #523: Multi-threading supported? + +- **URL:** https://github.com/SciSharp/NumSharp/issues/523 +- **State:** OPEN +- **Author:** @sawyermade +- **Created:** 2025-09-09T19:10:24Z +- **Updated:** 2025-09-09T19:10:24Z + +## Description + +I was wondering how multi-threading works with this, Numpy.NET doesnt allow it and you have to lock the GIL, since this is all in native C#, is "no real multi-threading" like in Numpy.NET a problem with this library? Thanks for your time and have a great day. diff --git a/examples/NeuralNetwork.NumSharp/NeuralNetwork.NumSharp.csproj b/examples/NeuralNetwork.NumSharp/NeuralNetwork.NumSharp.csproj index 1184d8bd4..03f646546 100644 --- a/examples/NeuralNetwork.NumSharp/NeuralNetwork.NumSharp.csproj +++ b/examples/NeuralNetwork.NumSharp/NeuralNetwork.NumSharp.csproj @@ -1,12 +1,12 @@  - netstandard2.0 + net8.0;net10.0 AnyCPU;x64 true Open.snk Debug;Release - 12.0 + latest diff --git a/src/NumSharp.Bitmap/NumSharp.Bitmap.csproj b/src/NumSharp.Bitmap/NumSharp.Bitmap.csproj index fe4dd8888..246261489 100644 --- a/src/NumSharp.Bitmap/NumSharp.Bitmap.csproj +++ b/src/NumSharp.Bitmap/NumSharp.Bitmap.csproj @@ -1,11 +1,11 @@  - netstandard2.0 + net8.0;net10.0 true true Eli Belash, Haiping Chen, Meinrad Recheis ../../packages - This package provides extensions for System.Drawing.Bitmap for creating NDArray and Bitmap with or without copying. + Windows-only extensions for System.Drawing.Bitmap for creating NDArray and Bitmap with or without copying. Uses GDI+ via System.Drawing.Common. https://github.com/SciSharp 2021 © SciSharp STACK Team https://github.com/SciSharp/NumSharp @@ -15,7 +15,7 @@ git Numpy, NumSharp, MachineLearning, Math, Scientific, Numeric, Mathlab, SciSharp - 12.0 + latest https://avatars3.githubusercontent.com/u/44989469?s=200&v=4 NumSharp.Bitmap NumSharp.Bitmap diff --git a/src/NumSharp.Bitmap/np_.extensions.cs b/src/NumSharp.Bitmap/np_.extensions.cs index 47d6e6081..89fc209a8 100644 --- a/src/NumSharp.Bitmap/np_.extensions.cs +++ b/src/NumSharp.Bitmap/np_.extensions.cs @@ -2,12 +2,14 @@ using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Imaging; +using System.Runtime.Versioning; using NumSharp.Backends; using NumSharp.Backends.Unmanaged; // ReSharper disable once CheckNamespace namespace NumSharp { + [SupportedOSPlatform("windows")] [SuppressMessage("ReSharper", "SuggestVarOrType_SimpleTypes")] [SuppressMessage("ReSharper", "UnusedMember.Global")] public static class np_ diff --git a/src/NumSharp.Core/APIs/np.load.cs b/src/NumSharp.Core/APIs/np.load.cs index b74addeb8..7fd9dee02 100644 --- a/src/NumSharp.Core/APIs/np.load.cs +++ b/src/NumSharp.Core/APIs/np.load.cs @@ -11,7 +11,7 @@ public static partial class np { #region NpyFormat - //Signature from numpy doc: + //Signature from numpy doc: // numpy.load(file, mmap_mode=None, allow_pickle=True, fix_imports=True, encoding='ASCII')[source] public static NDArray load(string path) { @@ -22,9 +22,7 @@ public static NDArray load(string path) public static NDArray load(Stream stream) { using (var reader = new BinaryReader(stream, System.Text.Encoding.ASCII -#if !NET35 && !NET40 , leaveOpen: true -#endif )) { int bytes; @@ -42,13 +40,9 @@ public static NDArray load(Stream stream) public static T Load(byte[] bytes) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable -#if !NET35 , IStructuralComparable, IStructuralEquatable -#endif { if (typeof(T).IsArray && (typeof(T).GetElementType().IsArray || typeof(T).GetElementType() == typeof(string))) return LoadJagged(bytes) as T; @@ -57,13 +51,9 @@ public static T Load(byte[] bytes) public static T Load(byte[] bytes, out T value) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable -#if !NET35 , IStructuralComparable, IStructuralEquatable -#endif { return value = Load(bytes); } @@ -71,26 +61,18 @@ public static T Load(byte[] bytes, out T value) public static T Load(string path, out T value) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable -#if !NET35 , IStructuralComparable, IStructuralEquatable -#endif { return value = Load(path); } public static T Load(Stream stream, out T value) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable -#if !NET35 , IStructuralComparable, IStructuralEquatable -#endif { return value = Load(stream); } @@ -98,13 +80,9 @@ public static T Load(Stream stream, out T value) public static T Load(string path) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable -#if !NET35 , IStructuralComparable, IStructuralEquatable -#endif { using (var stream = new FileStream(path, FileMode.Open)) return Load(stream); @@ -113,13 +91,9 @@ public static T Load(string path) public static T Load(Stream stream) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable -#if !NET35 , IStructuralComparable, IStructuralEquatable -#endif { if (typeof(T).IsArray && (typeof(T).GetElementType().IsArray || typeof(T).GetElementType() == typeof(string))) return LoadJagged(stream) as T; @@ -155,9 +129,7 @@ public static Array LoadJagged(string path) public static Array LoadMatrix(Stream stream) { using (var reader = new BinaryReader(stream, System.Text.Encoding.ASCII -#if !NET35 && !NET40 , leaveOpen: true -#endif )) { int bytes; @@ -178,9 +150,7 @@ public static Array LoadMatrix(Stream stream) public static Array LoadJagged(Stream stream, bool trim = true) { using (var reader = new BinaryReader(stream, System.Text.Encoding.ASCII -#if !NET35 && !NET40 , leaveOpen: true -#endif )) { int bytes; @@ -301,11 +271,7 @@ private static Array readStringMatrix(BinaryReader reader, Array matrix, int byt } } -#if NETSTANDARD1_4 - String s = new String((char*)b); -#else String s = new String((sbyte*)b); -#endif matrix.SetValue(value: s, indices: p); } } @@ -426,9 +392,7 @@ private static Type GetType(string dtype, out int bytes, out bool? isLittleEndia public static void Load_Npz(byte[] bytes, out T value) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { using (var dict = Load_Npz(bytes)) @@ -439,9 +403,7 @@ public static void Load_Npz(byte[] bytes, out T value) public static void Load_Npz(string path, out T value) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { using (var dict = Load_Npz(path)) @@ -452,9 +414,7 @@ public static void Load_Npz(string path, out T value) public static void Load_Npz(Stream stream, out T value) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { using (var dict = Load_Npz(stream)) @@ -465,9 +425,7 @@ public static void Load_Npz(Stream stream, out T value) public static NpzDictionary Load_Npz(byte[] bytes) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { return Load_Npz(new MemoryStream(bytes)); @@ -475,9 +433,7 @@ public static NpzDictionary Load_Npz(byte[] bytes) public static NpzDictionary Load_Npz(string path, out NpzDictionary value) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { return value = Load_Npz(new FileStream(path, FileMode.Open)); @@ -485,9 +441,7 @@ public static NpzDictionary Load_Npz(string path, out NpzDictionary val public static NpzDictionary Load_Npz(Stream stream, out NpzDictionary value) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { return value = Load_Npz(stream); @@ -495,9 +449,7 @@ public static NpzDictionary Load_Npz(Stream stream, out NpzDictionary v public static NpzDictionary Load_Npz(string path) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { return Load_Npz(new FileStream(path, FileMode.Open)); @@ -505,9 +457,7 @@ public static NpzDictionary Load_Npz(string path) public static NpzDictionary Load_Npz(Stream stream) where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { return new NpzDictionary(stream); diff --git a/src/NumSharp.Core/APIs/np.save.cs b/src/NumSharp.Core/APIs/np.save.cs index c4246d698..322ea173c 100644 --- a/src/NumSharp.Core/APIs/np.save.cs +++ b/src/NumSharp.Core/APIs/np.save.cs @@ -45,9 +45,7 @@ public static ulong Save(Array array, string path) public static ulong Save(Array array, Stream stream) { using (var writer = new BinaryWriter(stream -#if !NET35 && !NET40 , System.Text.Encoding.ASCII, leaveOpen: true -#endif )) { Type type; @@ -83,11 +81,7 @@ private static ulong writeValueMatrix(BinaryWriter reader, Array matrix, int byt Buffer.BlockCopy(matrix, 0, buffer, 0, buffer.Length); reader.Write(buffer, 0, buffer.Length); -#if NETSTANDARD1_4 - return (ulong)buffer.Length; -#else return (ulong)buffer.LongLength; -#endif } static IEnumerable Enumerate(Array a, int[] dimensions, int pos) @@ -117,11 +111,7 @@ private static ulong writeValueJagged(BinaryWriter reader, Array matrix, int byt Array.Clear(buffer, arr.Length, buffer.Length - buffer.Length); Buffer.BlockCopy(arr, 0, buffer, 0, buffer.Length); reader.Write(buffer, 0, buffer.Length); -#if NETSTANDARD1_4 - writtenBytes += (ulong)buffer.Length; -#else writtenBytes += (ulong)buffer.LongLength; -#endif } return writtenBytes; @@ -158,11 +148,7 @@ private static ulong writeStringMatrix(BinaryWriter reader, Array matrix, int by reader.Write(empty, 0, bytes); } -#if NETSTANDARD1_4 - writtenBytes += (ulong)buffer.Length; -#else writtenBytes += (ulong)buffer.LongLength; -#endif } } } @@ -233,9 +219,7 @@ private static string GetDtypeFromType(Array array, out Type type, out int bytes } else { -#pragma warning disable 618 // SizeOf would be Obsolete - bytes = Marshal.SizeOf(type); -#pragma warning restore 618 // SizeOf would be Obsolete + bytes = System.Runtime.InteropServices.Marshal.SizeOf(type); } if (type == typeof(bool)) diff --git a/src/NumSharp.Core/Assembly/Properties.cs b/src/NumSharp.Core/Assembly/Properties.cs index ec32c5f89..8929c54d5 100644 --- a/src/NumSharp.Core/Assembly/Properties.cs +++ b/src/NumSharp.Core/Assembly/Properties.cs @@ -3,4 +3,5 @@ [assembly: InternalsVisibleTo("NumSharp.UnitTest")] [assembly: InternalsVisibleTo("NumSharp.Benchmark")] [assembly: InternalsVisibleTo("TensorFlowNET.UnitTest")] +[assembly: InternalsVisibleTo("NumSharp.DotNetRunScript")] #endif diff --git a/src/NumSharp.Core/Backends/LAPACK/LAPACK.NetLib.cs b/src/NumSharp.Core/Backends/LAPACK/LAPACK.NetLib.cs deleted file mode 100644 index 0a7c8ef5b..000000000 --- a/src/NumSharp.Core/Backends/LAPACK/LAPACK.NetLib.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Runtime.InteropServices; - -namespace NumSharp.LAPACKProvider -{ - public static partial class NetLib - { - [DllImport("lapack")] - public static extern void dgesv_(ref int n, ref int nrhs, double[] a, ref int lda, int[] ipiv, double[] b, ref int ldb, ref int info); - - [DllImport("lapack")] - public static extern void dgeqrf_(ref int m, ref int n, double[] a, ref int lda, double[] tau, double[] work, ref int lwork, ref int info); - - [DllImport("lapack")] - public static extern void dorgqr_(ref int m, ref int n, ref int k, double[] a, ref int lda, double[] tau, double[] work, ref int lwork, ref int info); - - [DllImport("lapack")] - public static extern void dgelss_(ref int m, ref int n, ref int nrhs, double[] a, ref int lda, double[] b, ref int ldb, double[] s, ref double rcond, ref int rank, double[] work, ref int lwork, ref int info); - - // not working well - [DllImport("lapack", CallingConvention = CallingConvention.Cdecl), System.Security.SuppressUnmanagedCodeSecurity] - public static extern void dgelsy_(ref int m, ref int n, ref int nrhs, double[] a, ref int lda, double[] b, ref int ldb, int[] jpvt, ref double rcond, ref int rank, double[] work, ref int lwork, ref int info); - - [DllImport("lapack")] - public static extern void dgesvd_(char[] JOBU, char[] JOBVT, ref int M, ref int N, double[] A, ref int LDA, double[] S, double[] U, ref int LDU, double[] VT, ref int LDVT, double[] WORK, ref int LWORK, ref int INFO); - } -} diff --git a/src/NumSharp.Core/Backends/LAPACK/LAPACK.cs b/src/NumSharp.Core/Backends/LAPACK/LAPACK.cs deleted file mode 100644 index b0dcc67b3..000000000 --- a/src/NumSharp.Core/Backends/LAPACK/LAPACK.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace NumSharp -{ - public static partial class LAPACK - { - public static void dgesv_(ref int n, ref int nrhs, double[] a, ref int lda, int[] ipiv, double[] b, ref int ldb, ref int info) - { - switch (np.LAPACKProvider) - { - case LAPACKProviderType.NetLib: - { - LAPACKProvider.NetLib.dgesv_(ref n, ref nrhs, a, ref lda, ipiv, b, ref ldb, ref info); - break; - } - } - } - - public static void dgeqrf_(ref int m, ref int n, double[] a, ref int lda, double[] tau, double[] work, ref int lwork, ref int info) - { - switch (np.LAPACKProvider) - { - case LAPACKProviderType.NetLib: - { - LAPACKProvider.NetLib.dgeqrf_(ref m, ref n, a, ref lda, tau, work, ref lwork, ref info); - break; - } - } - } - - public static void dorgqr_(ref int m, ref int n, ref int k, double[] a, ref int lda, double[] tau, double[] work, ref int lwork, ref int info) - { - switch (np.LAPACKProvider) - { - case LAPACKProviderType.NetLib: - { - LAPACKProvider.NetLib.dorgqr_(ref m, ref n, ref k, a, ref lda, tau, work, ref lwork, ref info); - break; - } - } - } - - public static void dgelss_(ref int m, ref int n, ref int nrhs, double[] a, ref int lda, double[] b, ref int ldb, double[] s, ref double rcond, ref int rank, double[] work, ref int lwork, ref int info) - { - switch (np.LAPACKProvider) - { - case LAPACKProviderType.NetLib: - { - LAPACKProvider.NetLib.dgelss_(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, s, ref rcond, ref rank, work, ref lwork, ref info); - break; - } - } - } - - public static void dgesvd_(char[] JOBU, char[] JOBVT, ref int M, ref int N, double[] A, ref int LDA, double[] S, double[] U, ref int LDU, double[] VT, ref int LDVT, double[] WORK, ref int LWORK, ref int INFO) - { - switch (np.LAPACKProvider) - { - case LAPACKProviderType.NetLib: - { - LAPACKProvider.NetLib.dgesvd_(JOBU, JOBVT, ref M, ref N, A, ref LDA, S, U, ref LDU, VT, ref LDVT, WORK, ref LWORK, ref INFO); - break; - } - } - } - } -} diff --git a/src/NumSharp.Core/Backends/LAPACK/np.LAPACK.cs b/src/NumSharp.Core/Backends/LAPACK/np.LAPACK.cs deleted file mode 100644 index 63ea6556b..000000000 --- a/src/NumSharp.Core/Backends/LAPACK/np.LAPACK.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace NumSharp -{ - public enum LAPACKProviderType - { - NetLib - } - - public static partial class np - { - public static LAPACKProviderType LAPACKProvider = LAPACKProviderType.NetLib; - } -} diff --git a/src/NumSharp.Core/Backends/Unmanaged/Pooling/Fx.cs b/src/NumSharp.Core/Backends/Unmanaged/Pooling/Fx.cs index 61a08278f..28f6b5e01 100644 --- a/src/NumSharp.Core/Backends/Unmanaged/Pooling/Fx.cs +++ b/src/NumSharp.Core/Backends/Unmanaged/Pooling/Fx.cs @@ -4,7 +4,6 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; -using System.Runtime.Serialization; using System.Security; using System.Threading; using NumSharp.Utilities; @@ -937,28 +936,18 @@ void UnhandledExceptionFrame(uint error, uint bytesRead, NativeOverlapped* nativ } } - [Serializable] class InternalException : SystemException { public InternalException(string description) : base(description) { } - - protected InternalException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } } - [Serializable] class FatalInternalException : InternalException { public FatalInternalException(string description) : base(description) { } - - protected FatalInternalException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } } } } diff --git a/src/NumSharp.Core/Backends/Unmanaged/UnmanagedStorage.Slicing.cs b/src/NumSharp.Core/Backends/Unmanaged/UnmanagedStorage.Slicing.cs index 3e6216a1d..d33cf8793 100644 --- a/src/NumSharp.Core/Backends/Unmanaged/UnmanagedStorage.Slicing.cs +++ b/src/NumSharp.Core/Backends/Unmanaged/UnmanagedStorage.Slicing.cs @@ -96,9 +96,16 @@ private UnmanagedStorage GetViewInternal(params Slice[] slices) if (!_shape.IsRecursive && slices.All(s => Equals(Slice.All, s))) return Alias(); - //handle broadcasted shape + //handle broadcasted shape: materialize broadcast data into contiguous memory, + //then slice the contiguous result. We must use a Clean() shape (not the broadcast + //shape) so that strides match the contiguous data layout. Using _shape.Slice(slices) + //would attach broadcast strides [1,0] to contiguous data [3,1], causing wrong offsets. if (_shape.IsBroadcasted) - return Clone().Alias(_shape.Slice(slices)); + { + var clonedData = CloneData(); + var cleanShape = _shape.Clean(); + return new UnmanagedStorage(clonedData, cleanShape).GetViewInternal(slices); + } return Alias(_shape.Slice(slices)); } diff --git a/src/NumSharp.Core/Creation/np.dtype.cs b/src/NumSharp.Core/Creation/np.dtype.cs index 9fb2e6583..5bc97a047 100644 --- a/src/NumSharp.Core/Creation/np.dtype.cs +++ b/src/NumSharp.Core/Creation/np.dtype.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -10,7 +11,7 @@ namespace NumSharp /// https://docs.scipy.org/doc/numpy-1.16.0/reference/generated/numpy.dtype.html#numpy.dtype public class DType { - protected internal static readonly Dictionary _kind_list_map = new Dictionary() + protected internal static readonly FrozenDictionary _kind_list_map = new Dictionary() { {NPTypeCode.Complex, 'c'}, {NPTypeCode.Boolean, '?'}, @@ -26,7 +27,7 @@ public class DType {NPTypeCode.Single, 'f'}, {NPTypeCode.Decimal, 'f'}, {NPTypeCode.String, 'S'}, - }; + }.ToFrozenDictionary(); /// Initializes a new instance of the class. public DType(Type type) diff --git a/src/NumSharp.Core/Exceptions/NumSharpException.cs b/src/NumSharp.Core/Exceptions/NumSharpException.cs index 2fdb5a0ba..9deac6936 100644 --- a/src/NumSharp.Core/Exceptions/NumSharpException.cs +++ b/src/NumSharp.Core/Exceptions/NumSharpException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace NumSharp { @@ -11,14 +10,6 @@ public class NumSharpException : Exception, INumSharpException public NumSharpException() { } - /// Initializes a new instance of the class with serialized data. - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - /// The info parameter is null. - /// The class name is null or is zero (0). - protected NumSharpException(SerializationInfo info, StreamingContext context) : base(info, context) - { } - /// Initializes a new instance of the class with a specified error message. /// The message that describes the error. public NumSharpException(string message) : base(message) diff --git a/src/NumSharp.Core/Logic/np.find_common_type.cs b/src/NumSharp.Core/Logic/np.find_common_type.cs index a1dd8befd..ebd3f3804 100644 --- a/src/NumSharp.Core/Logic/np.find_common_type.cs +++ b/src/NumSharp.Core/Logic/np.find_common_type.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -39,390 +40,396 @@ public static partial class np #endregion - internal static readonly Dictionary<(Type, Type), Type> _typemap_arr_arr; - internal static readonly Dictionary<(NPTypeCode, NPTypeCode), NPTypeCode> _nptypemap_arr_arr; - internal static readonly Dictionary<(Type, Type), Type> _typemap_arr_scalar; - internal static readonly Dictionary<(NPTypeCode, NPTypeCode), NPTypeCode> _nptypemap_arr_scalar; + internal static readonly FrozenDictionary<(Type, Type), Type> _typemap_arr_arr; + internal static readonly FrozenDictionary<(NPTypeCode, NPTypeCode), NPTypeCode> _nptypemap_arr_arr; + internal static readonly FrozenDictionary<(Type, Type), Type> _typemap_arr_scalar; + internal static readonly FrozenDictionary<(NPTypeCode, NPTypeCode), NPTypeCode> _nptypemap_arr_scalar; [SuppressMessage("ReSharper", "UseObjectOrCollectionInitializer")] static np() { #region arr_arr - _typemap_arr_arr = new Dictionary<(Type, Type), Type>(180); - _typemap_arr_arr.Add((np.@bool, np.@bool), np.@bool); - _typemap_arr_arr.Add((np.@bool, np.uint8), np.uint8); - _typemap_arr_arr.Add((np.@bool, np.int16), np.int16); - _typemap_arr_arr.Add((np.@bool, np.uint16), np.uint16); - _typemap_arr_arr.Add((np.@bool, np.int32), np.int32); - _typemap_arr_arr.Add((np.@bool, np.uint32), np.uint32); - _typemap_arr_arr.Add((np.@bool, np.int64), np.int64); - _typemap_arr_arr.Add((np.@bool, np.uint64), np.uint64); - _typemap_arr_arr.Add((np.@bool, np.float32), np.float32); - _typemap_arr_arr.Add((np.@bool, np.float64), np.float64); - _typemap_arr_arr.Add((np.@bool, np.complex64), np.complex64); - _typemap_arr_arr.Add((np.@bool, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.@bool, np.@char), np.@char); - - _typemap_arr_arr.Add((np.uint8, np.@bool), np.uint8); - _typemap_arr_arr.Add((np.uint8, np.uint8), np.uint8); - _typemap_arr_arr.Add((np.uint8, np.int16), np.int16); - _typemap_arr_arr.Add((np.uint8, np.uint16), np.uint16); - _typemap_arr_arr.Add((np.uint8, np.int32), np.int32); - _typemap_arr_arr.Add((np.uint8, np.uint32), np.uint32); - _typemap_arr_arr.Add((np.uint8, np.int64), np.int64); - _typemap_arr_arr.Add((np.uint8, np.uint64), np.uint64); - _typemap_arr_arr.Add((np.uint8, np.float32), np.float32); - _typemap_arr_arr.Add((np.uint8, np.float64), np.float64); - _typemap_arr_arr.Add((np.uint8, np.complex64), np.complex64); - _typemap_arr_arr.Add((np.uint8, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.uint8, np.@char), np.uint8); - - _typemap_arr_arr.Add((np.@char, np.@char), np.@char); - _typemap_arr_arr.Add((np.@char, np.@bool), np.@char); - _typemap_arr_arr.Add((np.@char, np.uint8), np.uint8); - _typemap_arr_arr.Add((np.@char, np.int16), np.int16); - _typemap_arr_arr.Add((np.@char, np.uint16), np.uint16); - _typemap_arr_arr.Add((np.@char, np.int32), np.int32); - _typemap_arr_arr.Add((np.@char, np.uint32), np.uint32); - _typemap_arr_arr.Add((np.@char, np.int64), np.int64); - _typemap_arr_arr.Add((np.@char, np.uint64), np.uint64); - _typemap_arr_arr.Add((np.@char, np.float32), np.float32); - _typemap_arr_arr.Add((np.@char, np.float64), np.float64); - _typemap_arr_arr.Add((np.@char, np.complex64), np.complex64); - _typemap_arr_arr.Add((np.@char, np.@decimal), np.@decimal); - - _typemap_arr_arr.Add((np.int16, np.@bool), np.int16); - _typemap_arr_arr.Add((np.int16, np.uint8), np.int16); - _typemap_arr_arr.Add((np.int16, np.int16), np.int16); - _typemap_arr_arr.Add((np.int16, np.uint16), np.int32); - _typemap_arr_arr.Add((np.int16, np.int32), np.int32); - _typemap_arr_arr.Add((np.int16, np.uint32), np.int64); - _typemap_arr_arr.Add((np.int16, np.int64), np.int64); - _typemap_arr_arr.Add((np.int16, np.uint64), np.float64); - _typemap_arr_arr.Add((np.int16, np.float32), np.float32); - _typemap_arr_arr.Add((np.int16, np.float64), np.float64); - _typemap_arr_arr.Add((np.int16, np.complex64), np.complex64); - _typemap_arr_arr.Add((np.int16, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.int16, np.@char), np.int16); - - _typemap_arr_arr.Add((np.uint16, np.@bool), np.uint16); - _typemap_arr_arr.Add((np.uint16, np.uint8), np.uint16); - _typemap_arr_arr.Add((np.uint16, np.int16), np.int32); - _typemap_arr_arr.Add((np.uint16, np.uint16), np.uint16); - _typemap_arr_arr.Add((np.uint16, np.int32), np.int32); - _typemap_arr_arr.Add((np.uint16, np.uint32), np.uint32); - _typemap_arr_arr.Add((np.uint16, np.int64), np.int64); - _typemap_arr_arr.Add((np.uint16, np.uint64), np.uint64); - _typemap_arr_arr.Add((np.uint16, np.float32), np.float32); - _typemap_arr_arr.Add((np.uint16, np.float64), np.float64); - _typemap_arr_arr.Add((np.uint16, np.complex64), np.complex64); - _typemap_arr_arr.Add((np.uint16, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.uint16, np.@char), np.uint16); - - _typemap_arr_arr.Add((np.int32, np.@bool), np.int32); - _typemap_arr_arr.Add((np.int32, np.uint8), np.int32); - _typemap_arr_arr.Add((np.int32, np.int16), np.int32); - _typemap_arr_arr.Add((np.int32, np.uint16), np.int32); - _typemap_arr_arr.Add((np.int32, np.int32), np.int32); - _typemap_arr_arr.Add((np.int32, np.uint32), np.int64); - _typemap_arr_arr.Add((np.int32, np.int64), np.int64); - _typemap_arr_arr.Add((np.int32, np.uint64), np.float64); - _typemap_arr_arr.Add((np.int32, np.float32), np.float64); - _typemap_arr_arr.Add((np.int32, np.float64), np.float64); - _typemap_arr_arr.Add((np.int32, np.complex64), np.complex128); - _typemap_arr_arr.Add((np.int32, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.int32, np.@char), np.int32); - - _typemap_arr_arr.Add((np.uint32, np.@bool), np.uint32); - _typemap_arr_arr.Add((np.uint32, np.uint8), np.uint32); - _typemap_arr_arr.Add((np.uint32, np.int16), np.int64); - _typemap_arr_arr.Add((np.uint32, np.uint16), np.uint32); - _typemap_arr_arr.Add((np.uint32, np.int32), np.int64); - _typemap_arr_arr.Add((np.uint32, np.uint32), np.uint32); - _typemap_arr_arr.Add((np.uint32, np.int64), np.int64); - _typemap_arr_arr.Add((np.uint32, np.uint64), np.uint64); - _typemap_arr_arr.Add((np.uint32, np.float32), np.float64); - _typemap_arr_arr.Add((np.uint32, np.float64), np.float64); - _typemap_arr_arr.Add((np.uint32, np.complex64), np.complex128); - _typemap_arr_arr.Add((np.uint32, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.uint32, np.@char), np.uint32); - - _typemap_arr_arr.Add((np.int64, np.@bool), np.int64); - _typemap_arr_arr.Add((np.int64, np.uint8), np.int64); - _typemap_arr_arr.Add((np.int64, np.int16), np.int64); - _typemap_arr_arr.Add((np.int64, np.uint16), np.int64); - _typemap_arr_arr.Add((np.int64, np.int32), np.int64); - _typemap_arr_arr.Add((np.int64, np.uint32), np.int64); - _typemap_arr_arr.Add((np.int64, np.int64), np.int64); - _typemap_arr_arr.Add((np.int64, np.uint64), np.float64); - _typemap_arr_arr.Add((np.int64, np.float32), np.float64); - _typemap_arr_arr.Add((np.int64, np.float64), np.float64); - _typemap_arr_arr.Add((np.int64, np.complex64), np.complex128); - _typemap_arr_arr.Add((np.int64, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.int64, np.@char), np.int64); - - _typemap_arr_arr.Add((np.uint64, np.@bool), np.uint64); - _typemap_arr_arr.Add((np.uint64, np.uint8), np.uint64); - _typemap_arr_arr.Add((np.uint64, np.int16), np.float64); - _typemap_arr_arr.Add((np.uint64, np.uint16), np.uint64); - _typemap_arr_arr.Add((np.uint64, np.int32), np.float64); - _typemap_arr_arr.Add((np.uint64, np.uint32), np.uint64); - _typemap_arr_arr.Add((np.uint64, np.int64), np.float64); - _typemap_arr_arr.Add((np.uint64, np.uint64), np.uint64); - _typemap_arr_arr.Add((np.uint64, np.float32), np.float64); - _typemap_arr_arr.Add((np.uint64, np.float64), np.float64); - _typemap_arr_arr.Add((np.uint64, np.complex64), np.complex128); - _typemap_arr_arr.Add((np.uint64, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.uint64, np.@char), np.uint64); - - _typemap_arr_arr.Add((np.float32, np.@bool), np.float32); - _typemap_arr_arr.Add((np.float32, np.uint8), np.float32); - _typemap_arr_arr.Add((np.float32, np.int16), np.float32); - _typemap_arr_arr.Add((np.float32, np.uint16), np.float32); - _typemap_arr_arr.Add((np.float32, np.int32), np.float64); - _typemap_arr_arr.Add((np.float32, np.uint32), np.float64); - _typemap_arr_arr.Add((np.float32, np.int64), np.float64); - _typemap_arr_arr.Add((np.float32, np.uint64), np.float64); - _typemap_arr_arr.Add((np.float32, np.float32), np.float32); - _typemap_arr_arr.Add((np.float32, np.float64), np.float64); - _typemap_arr_arr.Add((np.float32, np.complex64), np.complex64); - _typemap_arr_arr.Add((np.float32, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.float32, np.@char), np.float32); - - _typemap_arr_arr.Add((np.float64, np.@bool), np.float64); - _typemap_arr_arr.Add((np.float64, np.uint8), np.float64); - _typemap_arr_arr.Add((np.float64, np.int16), np.float64); - _typemap_arr_arr.Add((np.float64, np.uint16), np.float64); - _typemap_arr_arr.Add((np.float64, np.int32), np.float64); - _typemap_arr_arr.Add((np.float64, np.uint32), np.float64); - _typemap_arr_arr.Add((np.float64, np.int64), np.float64); - _typemap_arr_arr.Add((np.float64, np.uint64), np.float64); - _typemap_arr_arr.Add((np.float64, np.float32), np.float64); - _typemap_arr_arr.Add((np.float64, np.float64), np.float64); - _typemap_arr_arr.Add((np.float64, np.complex64), np.complex128); - _typemap_arr_arr.Add((np.float64, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.float64, np.@char), np.float64); - - _typemap_arr_arr.Add((np.complex64, np.@bool), np.complex64); - _typemap_arr_arr.Add((np.complex64, np.uint8), np.complex64); - _typemap_arr_arr.Add((np.complex64, np.int16), np.complex64); - _typemap_arr_arr.Add((np.complex64, np.uint16), np.complex64); - _typemap_arr_arr.Add((np.complex64, np.int32), np.complex128); - _typemap_arr_arr.Add((np.complex64, np.uint32), np.complex128); - _typemap_arr_arr.Add((np.complex64, np.int64), np.complex128); - _typemap_arr_arr.Add((np.complex64, np.uint64), np.complex128); - _typemap_arr_arr.Add((np.complex64, np.float32), np.complex64); - _typemap_arr_arr.Add((np.complex64, np.float64), np.complex128); - _typemap_arr_arr.Add((np.complex64, np.complex64), np.complex64); - _typemap_arr_arr.Add((np.complex64, np.@decimal), np.complex64); - _typemap_arr_arr.Add((np.complex64, np.@char), np.complex64); - - _typemap_arr_arr.Add((np.@decimal, np.@bool), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.uint8), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.int16), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.uint16), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.int32), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.uint32), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.int64), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.uint64), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.float32), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.float64), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.complex64), np.complex128); - _typemap_arr_arr.Add((np.@decimal, np.@decimal), np.@decimal); - _typemap_arr_arr.Add((np.@decimal, np.@char), np.@decimal); - - _nptypemap_arr_arr = new Dictionary<(NPTypeCode, NPTypeCode), NPTypeCode>(_typemap_arr_arr.Count); - foreach (var tc in _typemap_arr_arr) _nptypemap_arr_arr[(tc.Key.Item1.GetTypeCode(), tc.Key.Item2.GetTypeCode())] = tc.Value.GetTypeCode(); + var typemap_arr_arr = new Dictionary<(Type, Type), Type>(180); + typemap_arr_arr.Add((np.@bool, np.@bool), np.@bool); + typemap_arr_arr.Add((np.@bool, np.uint8), np.uint8); + typemap_arr_arr.Add((np.@bool, np.int16), np.int16); + typemap_arr_arr.Add((np.@bool, np.uint16), np.uint16); + typemap_arr_arr.Add((np.@bool, np.int32), np.int32); + typemap_arr_arr.Add((np.@bool, np.uint32), np.uint32); + typemap_arr_arr.Add((np.@bool, np.int64), np.int64); + typemap_arr_arr.Add((np.@bool, np.uint64), np.uint64); + typemap_arr_arr.Add((np.@bool, np.float32), np.float32); + typemap_arr_arr.Add((np.@bool, np.float64), np.float64); + typemap_arr_arr.Add((np.@bool, np.complex64), np.complex64); + typemap_arr_arr.Add((np.@bool, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.@bool, np.@char), np.@char); + + typemap_arr_arr.Add((np.uint8, np.@bool), np.uint8); + typemap_arr_arr.Add((np.uint8, np.uint8), np.uint8); + typemap_arr_arr.Add((np.uint8, np.int16), np.int16); + typemap_arr_arr.Add((np.uint8, np.uint16), np.uint16); + typemap_arr_arr.Add((np.uint8, np.int32), np.int32); + typemap_arr_arr.Add((np.uint8, np.uint32), np.uint32); + typemap_arr_arr.Add((np.uint8, np.int64), np.int64); + typemap_arr_arr.Add((np.uint8, np.uint64), np.uint64); + typemap_arr_arr.Add((np.uint8, np.float32), np.float32); + typemap_arr_arr.Add((np.uint8, np.float64), np.float64); + typemap_arr_arr.Add((np.uint8, np.complex64), np.complex64); + typemap_arr_arr.Add((np.uint8, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.uint8, np.@char), np.uint8); + + typemap_arr_arr.Add((np.@char, np.@char), np.@char); + typemap_arr_arr.Add((np.@char, np.@bool), np.@char); + typemap_arr_arr.Add((np.@char, np.uint8), np.uint8); + typemap_arr_arr.Add((np.@char, np.int16), np.int16); + typemap_arr_arr.Add((np.@char, np.uint16), np.uint16); + typemap_arr_arr.Add((np.@char, np.int32), np.int32); + typemap_arr_arr.Add((np.@char, np.uint32), np.uint32); + typemap_arr_arr.Add((np.@char, np.int64), np.int64); + typemap_arr_arr.Add((np.@char, np.uint64), np.uint64); + typemap_arr_arr.Add((np.@char, np.float32), np.float32); + typemap_arr_arr.Add((np.@char, np.float64), np.float64); + typemap_arr_arr.Add((np.@char, np.complex64), np.complex64); + typemap_arr_arr.Add((np.@char, np.@decimal), np.@decimal); + + typemap_arr_arr.Add((np.int16, np.@bool), np.int16); + typemap_arr_arr.Add((np.int16, np.uint8), np.int16); + typemap_arr_arr.Add((np.int16, np.int16), np.int16); + typemap_arr_arr.Add((np.int16, np.uint16), np.int32); + typemap_arr_arr.Add((np.int16, np.int32), np.int32); + typemap_arr_arr.Add((np.int16, np.uint32), np.int64); + typemap_arr_arr.Add((np.int16, np.int64), np.int64); + typemap_arr_arr.Add((np.int16, np.uint64), np.float64); + typemap_arr_arr.Add((np.int16, np.float32), np.float32); + typemap_arr_arr.Add((np.int16, np.float64), np.float64); + typemap_arr_arr.Add((np.int16, np.complex64), np.complex64); + typemap_arr_arr.Add((np.int16, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.int16, np.@char), np.int16); + + typemap_arr_arr.Add((np.uint16, np.@bool), np.uint16); + typemap_arr_arr.Add((np.uint16, np.uint8), np.uint16); + typemap_arr_arr.Add((np.uint16, np.int16), np.int32); + typemap_arr_arr.Add((np.uint16, np.uint16), np.uint16); + typemap_arr_arr.Add((np.uint16, np.int32), np.int32); + typemap_arr_arr.Add((np.uint16, np.uint32), np.uint32); + typemap_arr_arr.Add((np.uint16, np.int64), np.int64); + typemap_arr_arr.Add((np.uint16, np.uint64), np.uint64); + typemap_arr_arr.Add((np.uint16, np.float32), np.float32); + typemap_arr_arr.Add((np.uint16, np.float64), np.float64); + typemap_arr_arr.Add((np.uint16, np.complex64), np.complex64); + typemap_arr_arr.Add((np.uint16, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.uint16, np.@char), np.uint16); + + typemap_arr_arr.Add((np.int32, np.@bool), np.int32); + typemap_arr_arr.Add((np.int32, np.uint8), np.int32); + typemap_arr_arr.Add((np.int32, np.int16), np.int32); + typemap_arr_arr.Add((np.int32, np.uint16), np.int32); + typemap_arr_arr.Add((np.int32, np.int32), np.int32); + typemap_arr_arr.Add((np.int32, np.uint32), np.int64); + typemap_arr_arr.Add((np.int32, np.int64), np.int64); + typemap_arr_arr.Add((np.int32, np.uint64), np.float64); + typemap_arr_arr.Add((np.int32, np.float32), np.float64); + typemap_arr_arr.Add((np.int32, np.float64), np.float64); + typemap_arr_arr.Add((np.int32, np.complex64), np.complex128); + typemap_arr_arr.Add((np.int32, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.int32, np.@char), np.int32); + + typemap_arr_arr.Add((np.uint32, np.@bool), np.uint32); + typemap_arr_arr.Add((np.uint32, np.uint8), np.uint32); + typemap_arr_arr.Add((np.uint32, np.int16), np.int64); + typemap_arr_arr.Add((np.uint32, np.uint16), np.uint32); + typemap_arr_arr.Add((np.uint32, np.int32), np.int64); + typemap_arr_arr.Add((np.uint32, np.uint32), np.uint32); + typemap_arr_arr.Add((np.uint32, np.int64), np.int64); + typemap_arr_arr.Add((np.uint32, np.uint64), np.uint64); + typemap_arr_arr.Add((np.uint32, np.float32), np.float64); + typemap_arr_arr.Add((np.uint32, np.float64), np.float64); + typemap_arr_arr.Add((np.uint32, np.complex64), np.complex128); + typemap_arr_arr.Add((np.uint32, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.uint32, np.@char), np.uint32); + + typemap_arr_arr.Add((np.int64, np.@bool), np.int64); + typemap_arr_arr.Add((np.int64, np.uint8), np.int64); + typemap_arr_arr.Add((np.int64, np.int16), np.int64); + typemap_arr_arr.Add((np.int64, np.uint16), np.int64); + typemap_arr_arr.Add((np.int64, np.int32), np.int64); + typemap_arr_arr.Add((np.int64, np.uint32), np.int64); + typemap_arr_arr.Add((np.int64, np.int64), np.int64); + typemap_arr_arr.Add((np.int64, np.uint64), np.float64); + typemap_arr_arr.Add((np.int64, np.float32), np.float64); + typemap_arr_arr.Add((np.int64, np.float64), np.float64); + typemap_arr_arr.Add((np.int64, np.complex64), np.complex128); + typemap_arr_arr.Add((np.int64, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.int64, np.@char), np.int64); + + typemap_arr_arr.Add((np.uint64, np.@bool), np.uint64); + typemap_arr_arr.Add((np.uint64, np.uint8), np.uint64); + typemap_arr_arr.Add((np.uint64, np.int16), np.float64); + typemap_arr_arr.Add((np.uint64, np.uint16), np.uint64); + typemap_arr_arr.Add((np.uint64, np.int32), np.float64); + typemap_arr_arr.Add((np.uint64, np.uint32), np.uint64); + typemap_arr_arr.Add((np.uint64, np.int64), np.float64); + typemap_arr_arr.Add((np.uint64, np.uint64), np.uint64); + typemap_arr_arr.Add((np.uint64, np.float32), np.float64); + typemap_arr_arr.Add((np.uint64, np.float64), np.float64); + typemap_arr_arr.Add((np.uint64, np.complex64), np.complex128); + typemap_arr_arr.Add((np.uint64, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.uint64, np.@char), np.uint64); + + typemap_arr_arr.Add((np.float32, np.@bool), np.float32); + typemap_arr_arr.Add((np.float32, np.uint8), np.float32); + typemap_arr_arr.Add((np.float32, np.int16), np.float32); + typemap_arr_arr.Add((np.float32, np.uint16), np.float32); + typemap_arr_arr.Add((np.float32, np.int32), np.float64); + typemap_arr_arr.Add((np.float32, np.uint32), np.float64); + typemap_arr_arr.Add((np.float32, np.int64), np.float64); + typemap_arr_arr.Add((np.float32, np.uint64), np.float64); + typemap_arr_arr.Add((np.float32, np.float32), np.float32); + typemap_arr_arr.Add((np.float32, np.float64), np.float64); + typemap_arr_arr.Add((np.float32, np.complex64), np.complex64); + typemap_arr_arr.Add((np.float32, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.float32, np.@char), np.float32); + + typemap_arr_arr.Add((np.float64, np.@bool), np.float64); + typemap_arr_arr.Add((np.float64, np.uint8), np.float64); + typemap_arr_arr.Add((np.float64, np.int16), np.float64); + typemap_arr_arr.Add((np.float64, np.uint16), np.float64); + typemap_arr_arr.Add((np.float64, np.int32), np.float64); + typemap_arr_arr.Add((np.float64, np.uint32), np.float64); + typemap_arr_arr.Add((np.float64, np.int64), np.float64); + typemap_arr_arr.Add((np.float64, np.uint64), np.float64); + typemap_arr_arr.Add((np.float64, np.float32), np.float64); + typemap_arr_arr.Add((np.float64, np.float64), np.float64); + typemap_arr_arr.Add((np.float64, np.complex64), np.complex128); + typemap_arr_arr.Add((np.float64, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.float64, np.@char), np.float64); + + typemap_arr_arr.Add((np.complex64, np.@bool), np.complex64); + typemap_arr_arr.Add((np.complex64, np.uint8), np.complex64); + typemap_arr_arr.Add((np.complex64, np.int16), np.complex64); + typemap_arr_arr.Add((np.complex64, np.uint16), np.complex64); + typemap_arr_arr.Add((np.complex64, np.int32), np.complex128); + typemap_arr_arr.Add((np.complex64, np.uint32), np.complex128); + typemap_arr_arr.Add((np.complex64, np.int64), np.complex128); + typemap_arr_arr.Add((np.complex64, np.uint64), np.complex128); + typemap_arr_arr.Add((np.complex64, np.float32), np.complex64); + typemap_arr_arr.Add((np.complex64, np.float64), np.complex128); + typemap_arr_arr.Add((np.complex64, np.complex64), np.complex64); + typemap_arr_arr.Add((np.complex64, np.@decimal), np.complex64); + typemap_arr_arr.Add((np.complex64, np.@char), np.complex64); + + typemap_arr_arr.Add((np.@decimal, np.@bool), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.uint8), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.int16), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.uint16), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.int32), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.uint32), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.int64), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.uint64), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.float32), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.float64), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.complex64), np.complex128); + typemap_arr_arr.Add((np.@decimal, np.@decimal), np.@decimal); + typemap_arr_arr.Add((np.@decimal, np.@char), np.@decimal); + + _typemap_arr_arr = typemap_arr_arr.ToFrozenDictionary(); + + var nptypemap_arr_arr = new Dictionary<(NPTypeCode, NPTypeCode), NPTypeCode>(typemap_arr_arr.Count); + foreach (var tc in typemap_arr_arr) nptypemap_arr_arr[(tc.Key.Item1.GetTypeCode(), tc.Key.Item2.GetTypeCode())] = tc.Value.GetTypeCode(); + _nptypemap_arr_arr = nptypemap_arr_arr.ToFrozenDictionary(); #endregion #region arr_scalar - _typemap_arr_scalar = new Dictionary<(Type, Type), Type>(); - _typemap_arr_scalar.Add((np.@bool, np.@bool), np.@bool); - _typemap_arr_scalar.Add((np.@bool, np.uint8), np.uint8); - _typemap_arr_scalar.Add((np.@bool, np.int16), np.int16); - _typemap_arr_scalar.Add((np.@bool, np.uint16), np.uint16); - _typemap_arr_scalar.Add((np.@bool, np.int32), np.int32); - _typemap_arr_scalar.Add((np.@bool, np.uint32), np.uint32); - _typemap_arr_scalar.Add((np.@bool, np.int64), np.int64); - _typemap_arr_scalar.Add((np.@bool, np.uint64), np.uint64); - _typemap_arr_scalar.Add((np.@bool, np.float32), np.float32); - _typemap_arr_scalar.Add((np.@bool, np.float64), np.float64); - _typemap_arr_scalar.Add((np.@bool, np.complex64), np.complex64); - - _typemap_arr_scalar.Add((np.uint8, np.@bool), np.uint8); - _typemap_arr_scalar.Add((np.uint8, np.uint8), np.uint8); - _typemap_arr_scalar.Add((np.uint8, np.@char), np.uint8); - _typemap_arr_scalar.Add((np.uint8, np.int16), np.int16); - _typemap_arr_scalar.Add((np.uint8, np.uint16), np.uint8); - _typemap_arr_scalar.Add((np.uint8, np.int32), np.int32); - _typemap_arr_scalar.Add((np.uint8, np.uint32), np.uint8); - _typemap_arr_scalar.Add((np.uint8, np.int64), np.int64); - _typemap_arr_scalar.Add((np.uint8, np.uint64), np.uint8); - _typemap_arr_scalar.Add((np.uint8, np.float32), np.float32); - _typemap_arr_scalar.Add((np.uint8, np.float64), np.float64); - _typemap_arr_scalar.Add((np.uint8, np.complex64), np.complex64); - - _typemap_arr_scalar.Add((np.@char, np.@char), np.@char); - _typemap_arr_scalar.Add((np.@char, np.@bool), np.@char); - _typemap_arr_scalar.Add((np.@char, np.uint8), np.@char); - _typemap_arr_scalar.Add((np.@char, np.int16), np.int16); - _typemap_arr_scalar.Add((np.@char, np.uint16), np.uint16); - _typemap_arr_scalar.Add((np.@char, np.int32), np.int32); - _typemap_arr_scalar.Add((np.@char, np.uint32), np.uint32); - _typemap_arr_scalar.Add((np.@char, np.int64), np.int64); - _typemap_arr_scalar.Add((np.@char, np.uint64), np.uint64); - _typemap_arr_scalar.Add((np.@char, np.float32), np.float32); - _typemap_arr_scalar.Add((np.@char, np.float64), np.float64); - _typemap_arr_scalar.Add((np.@char, np.complex64), np.complex64); - - _typemap_arr_scalar.Add((np.int16, np.@bool), np.int16); - _typemap_arr_scalar.Add((np.int16, np.uint8), np.int16); - _typemap_arr_scalar.Add((np.int16, np.@char), np.int16); - _typemap_arr_scalar.Add((np.int16, np.int16), np.int16); - _typemap_arr_scalar.Add((np.int16, np.uint16), np.int16); - _typemap_arr_scalar.Add((np.int16, np.int32), np.int16); - _typemap_arr_scalar.Add((np.int16, np.uint32), np.int16); - _typemap_arr_scalar.Add((np.int16, np.int64), np.int16); - _typemap_arr_scalar.Add((np.int16, np.uint64), np.int16); - _typemap_arr_scalar.Add((np.int16, np.float32), np.float32); - _typemap_arr_scalar.Add((np.int16, np.float64), np.float64); - _typemap_arr_scalar.Add((np.int16, np.complex64), np.complex64); - - _typemap_arr_scalar.Add((np.uint16, np.@bool), np.uint16); - _typemap_arr_scalar.Add((np.uint16, np.uint8), np.uint16); - _typemap_arr_scalar.Add((np.uint16, np.@char), np.uint16); - _typemap_arr_scalar.Add((np.uint16, np.int16), np.int32); - _typemap_arr_scalar.Add((np.uint16, np.uint16), np.uint16); - _typemap_arr_scalar.Add((np.uint16, np.int32), np.int32); - _typemap_arr_scalar.Add((np.uint16, np.uint32), np.uint16); - _typemap_arr_scalar.Add((np.uint16, np.int64), np.int64); - _typemap_arr_scalar.Add((np.uint16, np.uint64), np.uint16); - _typemap_arr_scalar.Add((np.uint16, np.float32), np.float32); - _typemap_arr_scalar.Add((np.uint16, np.float64), np.float64); - _typemap_arr_scalar.Add((np.uint16, np.complex64), np.complex64); - - _typemap_arr_scalar.Add((np.int32, np.@bool), np.int32); - _typemap_arr_scalar.Add((np.int32, np.uint8), np.int32); - _typemap_arr_scalar.Add((np.int32, np.@char), np.int32); - _typemap_arr_scalar.Add((np.int32, np.int16), np.int32); - _typemap_arr_scalar.Add((np.int32, np.uint16), np.int32); - _typemap_arr_scalar.Add((np.int32, np.int32), np.int32); - _typemap_arr_scalar.Add((np.int32, np.uint32), np.int32); - _typemap_arr_scalar.Add((np.int32, np.int64), np.int32); - _typemap_arr_scalar.Add((np.int32, np.uint64), np.int32); - _typemap_arr_scalar.Add((np.int32, np.float32), np.float64); - _typemap_arr_scalar.Add((np.int32, np.float64), np.float64); - _typemap_arr_scalar.Add((np.int32, np.complex64), np.complex128); - - _typemap_arr_scalar.Add((np.uint32, np.@bool), np.uint32); - _typemap_arr_scalar.Add((np.uint32, np.uint8), np.uint32); - _typemap_arr_scalar.Add((np.uint32, np.@char), np.uint32); - _typemap_arr_scalar.Add((np.uint32, np.int16), np.int64); - _typemap_arr_scalar.Add((np.uint32, np.uint16), np.uint32); - _typemap_arr_scalar.Add((np.uint32, np.int32), np.int64); - _typemap_arr_scalar.Add((np.uint32, np.uint32), np.uint32); - _typemap_arr_scalar.Add((np.uint32, np.int64), np.int64); - _typemap_arr_scalar.Add((np.uint32, np.uint64), np.uint32); - _typemap_arr_scalar.Add((np.uint32, np.float32), np.float64); - _typemap_arr_scalar.Add((np.uint32, np.float64), np.float64); - _typemap_arr_scalar.Add((np.uint32, np.complex64), np.complex128); - - _typemap_arr_scalar.Add((np.int64, np.@bool), np.int64); - _typemap_arr_scalar.Add((np.int64, np.uint8), np.int64); - _typemap_arr_scalar.Add((np.int64, np.@char), np.int64); - _typemap_arr_scalar.Add((np.int64, np.int16), np.int64); - _typemap_arr_scalar.Add((np.int64, np.uint16), np.int64); - _typemap_arr_scalar.Add((np.int64, np.int32), np.int64); - _typemap_arr_scalar.Add((np.int64, np.uint32), np.int64); - _typemap_arr_scalar.Add((np.int64, np.int64), np.int64); - _typemap_arr_scalar.Add((np.int64, np.uint64), np.int64); - _typemap_arr_scalar.Add((np.int64, np.float32), np.float64); - _typemap_arr_scalar.Add((np.int64, np.float64), np.float64); - _typemap_arr_scalar.Add((np.int64, np.complex64), np.complex128); - - _typemap_arr_scalar.Add((np.uint64, np.@bool), np.uint64); - _typemap_arr_scalar.Add((np.uint64, np.uint8), np.uint64); - _typemap_arr_scalar.Add((np.uint64, np.@char), np.uint64); - _typemap_arr_scalar.Add((np.uint64, np.int16), np.float64); - _typemap_arr_scalar.Add((np.uint64, np.uint16), np.uint64); - _typemap_arr_scalar.Add((np.uint64, np.int32), np.float64); - _typemap_arr_scalar.Add((np.uint64, np.uint32), np.uint64); - _typemap_arr_scalar.Add((np.uint64, np.int64), np.float64); - _typemap_arr_scalar.Add((np.uint64, np.uint64), np.uint64); - _typemap_arr_scalar.Add((np.uint64, np.float32), np.float64); - _typemap_arr_scalar.Add((np.uint64, np.float64), np.float64); - _typemap_arr_scalar.Add((np.uint64, np.complex64), np.complex128); - - _typemap_arr_scalar.Add((np.float32, np.@bool), np.float32); - _typemap_arr_scalar.Add((np.float32, np.uint8), np.float32); - _typemap_arr_scalar.Add((np.float32, np.@char), np.float32); - _typemap_arr_scalar.Add((np.float32, np.int16), np.float32); - _typemap_arr_scalar.Add((np.float32, np.uint16), np.float32); - _typemap_arr_scalar.Add((np.float32, np.int32), np.float32); - _typemap_arr_scalar.Add((np.float32, np.uint32), np.float32); - _typemap_arr_scalar.Add((np.float32, np.int64), np.float32); - _typemap_arr_scalar.Add((np.float32, np.uint64), np.float32); - _typemap_arr_scalar.Add((np.float32, np.float32), np.float32); - _typemap_arr_scalar.Add((np.float32, np.float64), np.float32); - _typemap_arr_scalar.Add((np.float32, np.complex64), np.complex64); - - _typemap_arr_scalar.Add((np.float64, np.@bool), np.float64); - _typemap_arr_scalar.Add((np.float64, np.uint8), np.float64); - _typemap_arr_scalar.Add((np.float64, np.@char), np.float64); - _typemap_arr_scalar.Add((np.float64, np.int16), np.float64); - _typemap_arr_scalar.Add((np.float64, np.uint16), np.float64); - _typemap_arr_scalar.Add((np.float64, np.int32), np.float64); - _typemap_arr_scalar.Add((np.float64, np.uint32), np.float64); - _typemap_arr_scalar.Add((np.float64, np.int64), np.float64); - _typemap_arr_scalar.Add((np.float64, np.uint64), np.float64); - _typemap_arr_scalar.Add((np.float64, np.float32), np.float64); - _typemap_arr_scalar.Add((np.float64, np.float64), np.float64); - _typemap_arr_scalar.Add((np.float64, np.complex64), np.complex128); - - _typemap_arr_scalar.Add((np.complex64, np.@bool), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.uint8), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.@char), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.int16), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.uint16), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.int32), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.uint32), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.int64), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.uint64), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.float32), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.float64), np.complex64); - _typemap_arr_scalar.Add((np.complex64, np.complex64), np.complex64); - - _typemap_arr_scalar.Add((np.@decimal, np.@bool), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.uint8), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.@char), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.int16), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.uint16), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.int32), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.uint32), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.int64), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.uint64), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.float32), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.float64), np.@decimal); - _typemap_arr_scalar.Add((np.@decimal, np.complex64), np.complex128); - _typemap_arr_scalar.Add((np.@decimal, np.@decimal), np.@decimal); - _typemap_arr_scalar.Add((np.@bool, np.@decimal), np.@bool); - _typemap_arr_scalar.Add((np.uint8, np.@decimal), np.uint8); - _typemap_arr_scalar.Add((np.@char, np.@decimal), np.@char); - _typemap_arr_scalar.Add((np.int16, np.@decimal), np.int16); - _typemap_arr_scalar.Add((np.uint16, np.@decimal), np.uint16); - _typemap_arr_scalar.Add((np.int32, np.@decimal), np.int32); - _typemap_arr_scalar.Add((np.uint32, np.@decimal), np.uint32); - _typemap_arr_scalar.Add((np.int64, np.@decimal), np.int64); - _typemap_arr_scalar.Add((np.uint64, np.@decimal), np.uint64); - _typemap_arr_scalar.Add((np.float32, np.@decimal), np.float32); - _typemap_arr_scalar.Add((np.float64, np.@decimal), np.float64); - _typemap_arr_scalar.Add((np.complex64, np.@decimal), np.complex128); - - _nptypemap_arr_scalar = new Dictionary<(NPTypeCode, NPTypeCode), NPTypeCode>(_typemap_arr_scalar.Count); - foreach (var tc in _typemap_arr_scalar) _nptypemap_arr_scalar[(tc.Key.Item1.GetTypeCode(), tc.Key.Item2.GetTypeCode())] = tc.Value.GetTypeCode(); + var typemap_arr_scalar = new Dictionary<(Type, Type), Type>(); + typemap_arr_scalar.Add((np.@bool, np.@bool), np.@bool); + typemap_arr_scalar.Add((np.@bool, np.uint8), np.uint8); + typemap_arr_scalar.Add((np.@bool, np.int16), np.int16); + typemap_arr_scalar.Add((np.@bool, np.uint16), np.uint16); + typemap_arr_scalar.Add((np.@bool, np.int32), np.int32); + typemap_arr_scalar.Add((np.@bool, np.uint32), np.uint32); + typemap_arr_scalar.Add((np.@bool, np.int64), np.int64); + typemap_arr_scalar.Add((np.@bool, np.uint64), np.uint64); + typemap_arr_scalar.Add((np.@bool, np.float32), np.float32); + typemap_arr_scalar.Add((np.@bool, np.float64), np.float64); + typemap_arr_scalar.Add((np.@bool, np.complex64), np.complex64); + + typemap_arr_scalar.Add((np.uint8, np.@bool), np.uint8); + typemap_arr_scalar.Add((np.uint8, np.uint8), np.uint8); + typemap_arr_scalar.Add((np.uint8, np.@char), np.uint8); + typemap_arr_scalar.Add((np.uint8, np.int16), np.int16); + typemap_arr_scalar.Add((np.uint8, np.uint16), np.uint8); + typemap_arr_scalar.Add((np.uint8, np.int32), np.int32); + typemap_arr_scalar.Add((np.uint8, np.uint32), np.uint8); + typemap_arr_scalar.Add((np.uint8, np.int64), np.int64); + typemap_arr_scalar.Add((np.uint8, np.uint64), np.uint8); + typemap_arr_scalar.Add((np.uint8, np.float32), np.float32); + typemap_arr_scalar.Add((np.uint8, np.float64), np.float64); + typemap_arr_scalar.Add((np.uint8, np.complex64), np.complex64); + + typemap_arr_scalar.Add((np.@char, np.@char), np.@char); + typemap_arr_scalar.Add((np.@char, np.@bool), np.@char); + typemap_arr_scalar.Add((np.@char, np.uint8), np.@char); + typemap_arr_scalar.Add((np.@char, np.int16), np.int16); + typemap_arr_scalar.Add((np.@char, np.uint16), np.uint16); + typemap_arr_scalar.Add((np.@char, np.int32), np.int32); + typemap_arr_scalar.Add((np.@char, np.uint32), np.uint32); + typemap_arr_scalar.Add((np.@char, np.int64), np.int64); + typemap_arr_scalar.Add((np.@char, np.uint64), np.uint64); + typemap_arr_scalar.Add((np.@char, np.float32), np.float32); + typemap_arr_scalar.Add((np.@char, np.float64), np.float64); + typemap_arr_scalar.Add((np.@char, np.complex64), np.complex64); + + typemap_arr_scalar.Add((np.int16, np.@bool), np.int16); + typemap_arr_scalar.Add((np.int16, np.uint8), np.int16); + typemap_arr_scalar.Add((np.int16, np.@char), np.int16); + typemap_arr_scalar.Add((np.int16, np.int16), np.int16); + typemap_arr_scalar.Add((np.int16, np.uint16), np.int16); + typemap_arr_scalar.Add((np.int16, np.int32), np.int16); + typemap_arr_scalar.Add((np.int16, np.uint32), np.int16); + typemap_arr_scalar.Add((np.int16, np.int64), np.int16); + typemap_arr_scalar.Add((np.int16, np.uint64), np.int16); + typemap_arr_scalar.Add((np.int16, np.float32), np.float32); + typemap_arr_scalar.Add((np.int16, np.float64), np.float64); + typemap_arr_scalar.Add((np.int16, np.complex64), np.complex64); + + typemap_arr_scalar.Add((np.uint16, np.@bool), np.uint16); + typemap_arr_scalar.Add((np.uint16, np.uint8), np.uint16); + typemap_arr_scalar.Add((np.uint16, np.@char), np.uint16); + typemap_arr_scalar.Add((np.uint16, np.int16), np.int32); + typemap_arr_scalar.Add((np.uint16, np.uint16), np.uint16); + typemap_arr_scalar.Add((np.uint16, np.int32), np.int32); + typemap_arr_scalar.Add((np.uint16, np.uint32), np.uint16); + typemap_arr_scalar.Add((np.uint16, np.int64), np.int64); + typemap_arr_scalar.Add((np.uint16, np.uint64), np.uint16); + typemap_arr_scalar.Add((np.uint16, np.float32), np.float32); + typemap_arr_scalar.Add((np.uint16, np.float64), np.float64); + typemap_arr_scalar.Add((np.uint16, np.complex64), np.complex64); + + typemap_arr_scalar.Add((np.int32, np.@bool), np.int32); + typemap_arr_scalar.Add((np.int32, np.uint8), np.int32); + typemap_arr_scalar.Add((np.int32, np.@char), np.int32); + typemap_arr_scalar.Add((np.int32, np.int16), np.int32); + typemap_arr_scalar.Add((np.int32, np.uint16), np.int32); + typemap_arr_scalar.Add((np.int32, np.int32), np.int32); + typemap_arr_scalar.Add((np.int32, np.uint32), np.int32); + typemap_arr_scalar.Add((np.int32, np.int64), np.int32); + typemap_arr_scalar.Add((np.int32, np.uint64), np.int32); + typemap_arr_scalar.Add((np.int32, np.float32), np.float64); + typemap_arr_scalar.Add((np.int32, np.float64), np.float64); + typemap_arr_scalar.Add((np.int32, np.complex64), np.complex128); + + typemap_arr_scalar.Add((np.uint32, np.@bool), np.uint32); + typemap_arr_scalar.Add((np.uint32, np.uint8), np.uint32); + typemap_arr_scalar.Add((np.uint32, np.@char), np.uint32); + typemap_arr_scalar.Add((np.uint32, np.int16), np.int64); + typemap_arr_scalar.Add((np.uint32, np.uint16), np.uint32); + typemap_arr_scalar.Add((np.uint32, np.int32), np.int64); + typemap_arr_scalar.Add((np.uint32, np.uint32), np.uint32); + typemap_arr_scalar.Add((np.uint32, np.int64), np.int64); + typemap_arr_scalar.Add((np.uint32, np.uint64), np.uint32); + typemap_arr_scalar.Add((np.uint32, np.float32), np.float64); + typemap_arr_scalar.Add((np.uint32, np.float64), np.float64); + typemap_arr_scalar.Add((np.uint32, np.complex64), np.complex128); + + typemap_arr_scalar.Add((np.int64, np.@bool), np.int64); + typemap_arr_scalar.Add((np.int64, np.uint8), np.int64); + typemap_arr_scalar.Add((np.int64, np.@char), np.int64); + typemap_arr_scalar.Add((np.int64, np.int16), np.int64); + typemap_arr_scalar.Add((np.int64, np.uint16), np.int64); + typemap_arr_scalar.Add((np.int64, np.int32), np.int64); + typemap_arr_scalar.Add((np.int64, np.uint32), np.int64); + typemap_arr_scalar.Add((np.int64, np.int64), np.int64); + typemap_arr_scalar.Add((np.int64, np.uint64), np.int64); + typemap_arr_scalar.Add((np.int64, np.float32), np.float64); + typemap_arr_scalar.Add((np.int64, np.float64), np.float64); + typemap_arr_scalar.Add((np.int64, np.complex64), np.complex128); + + typemap_arr_scalar.Add((np.uint64, np.@bool), np.uint64); + typemap_arr_scalar.Add((np.uint64, np.uint8), np.uint64); + typemap_arr_scalar.Add((np.uint64, np.@char), np.uint64); + typemap_arr_scalar.Add((np.uint64, np.int16), np.float64); + typemap_arr_scalar.Add((np.uint64, np.uint16), np.uint64); + typemap_arr_scalar.Add((np.uint64, np.int32), np.float64); + typemap_arr_scalar.Add((np.uint64, np.uint32), np.uint64); + typemap_arr_scalar.Add((np.uint64, np.int64), np.float64); + typemap_arr_scalar.Add((np.uint64, np.uint64), np.uint64); + typemap_arr_scalar.Add((np.uint64, np.float32), np.float64); + typemap_arr_scalar.Add((np.uint64, np.float64), np.float64); + typemap_arr_scalar.Add((np.uint64, np.complex64), np.complex128); + + typemap_arr_scalar.Add((np.float32, np.@bool), np.float32); + typemap_arr_scalar.Add((np.float32, np.uint8), np.float32); + typemap_arr_scalar.Add((np.float32, np.@char), np.float32); + typemap_arr_scalar.Add((np.float32, np.int16), np.float32); + typemap_arr_scalar.Add((np.float32, np.uint16), np.float32); + typemap_arr_scalar.Add((np.float32, np.int32), np.float32); + typemap_arr_scalar.Add((np.float32, np.uint32), np.float32); + typemap_arr_scalar.Add((np.float32, np.int64), np.float32); + typemap_arr_scalar.Add((np.float32, np.uint64), np.float32); + typemap_arr_scalar.Add((np.float32, np.float32), np.float32); + typemap_arr_scalar.Add((np.float32, np.float64), np.float32); + typemap_arr_scalar.Add((np.float32, np.complex64), np.complex64); + + typemap_arr_scalar.Add((np.float64, np.@bool), np.float64); + typemap_arr_scalar.Add((np.float64, np.uint8), np.float64); + typemap_arr_scalar.Add((np.float64, np.@char), np.float64); + typemap_arr_scalar.Add((np.float64, np.int16), np.float64); + typemap_arr_scalar.Add((np.float64, np.uint16), np.float64); + typemap_arr_scalar.Add((np.float64, np.int32), np.float64); + typemap_arr_scalar.Add((np.float64, np.uint32), np.float64); + typemap_arr_scalar.Add((np.float64, np.int64), np.float64); + typemap_arr_scalar.Add((np.float64, np.uint64), np.float64); + typemap_arr_scalar.Add((np.float64, np.float32), np.float64); + typemap_arr_scalar.Add((np.float64, np.float64), np.float64); + typemap_arr_scalar.Add((np.float64, np.complex64), np.complex128); + + typemap_arr_scalar.Add((np.complex64, np.@bool), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.uint8), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.@char), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.int16), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.uint16), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.int32), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.uint32), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.int64), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.uint64), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.float32), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.float64), np.complex64); + typemap_arr_scalar.Add((np.complex64, np.complex64), np.complex64); + + typemap_arr_scalar.Add((np.@decimal, np.@bool), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.uint8), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.@char), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.int16), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.uint16), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.int32), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.uint32), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.int64), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.uint64), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.float32), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.float64), np.@decimal); + typemap_arr_scalar.Add((np.@decimal, np.complex64), np.complex128); + typemap_arr_scalar.Add((np.@decimal, np.@decimal), np.@decimal); + typemap_arr_scalar.Add((np.@bool, np.@decimal), np.@bool); + typemap_arr_scalar.Add((np.uint8, np.@decimal), np.uint8); + typemap_arr_scalar.Add((np.@char, np.@decimal), np.@char); + typemap_arr_scalar.Add((np.int16, np.@decimal), np.int16); + typemap_arr_scalar.Add((np.uint16, np.@decimal), np.uint16); + typemap_arr_scalar.Add((np.int32, np.@decimal), np.int32); + typemap_arr_scalar.Add((np.uint32, np.@decimal), np.uint32); + typemap_arr_scalar.Add((np.int64, np.@decimal), np.int64); + typemap_arr_scalar.Add((np.uint64, np.@decimal), np.uint64); + typemap_arr_scalar.Add((np.float32, np.@decimal), np.float32); + typemap_arr_scalar.Add((np.float64, np.@decimal), np.float64); + typemap_arr_scalar.Add((np.complex64, np.@decimal), np.complex128); + + _typemap_arr_scalar = typemap_arr_scalar.ToFrozenDictionary(); + + var nptypemap_arr_scalar = new Dictionary<(NPTypeCode, NPTypeCode), NPTypeCode>(typemap_arr_scalar.Count); + foreach (var tc in typemap_arr_scalar) nptypemap_arr_scalar[(tc.Key.Item1.GetTypeCode(), tc.Key.Item2.GetTypeCode())] = tc.Value.GetTypeCode(); + _nptypemap_arr_scalar = nptypemap_arr_scalar.ToFrozenDictionary(); #endregion } diff --git a/src/NumSharp.Core/NumSharp.Core.csproj b/src/NumSharp.Core/NumSharp.Core.csproj index bb150e4f1..9bcd97d0a 100644 --- a/src/NumSharp.Core/NumSharp.Core.csproj +++ b/src/NumSharp.Core/NumSharp.Core.csproj @@ -1,6 +1,6 @@  - netstandard2.0 + net8.0;net10.0 true true Eli Belash, Haiping Chen, Meinrad Recheis, Deepak Kumar Battini @@ -15,7 +15,7 @@ git Numpy, NumSharp, MachineLearning, Math, Scientific, Numeric, Mathlab, SciSharp - 12.0 + latest https://avatars3.githubusercontent.com/u/44989469?s=200&v=4 NumSharp NumSharp @@ -79,10 +79,7 @@ - - - - + diff --git a/src/NumSharp.Core/RandomSampling/NativeRandomState.cs b/src/NumSharp.Core/RandomSampling/NativeRandomState.cs index b6614dc75..2013a4423 100644 --- a/src/NumSharp.Core/RandomSampling/NativeRandomState.cs +++ b/src/NumSharp.Core/RandomSampling/NativeRandomState.cs @@ -18,7 +18,6 @@ public static Randomizer Restore(this NativeRandomState state) /// /// Represents the stored state of . /// - [Serializable] public struct NativeRandomState { public readonly byte[] State; diff --git a/src/NumSharp.Core/RandomSampling/Randomizer.cs b/src/NumSharp.Core/RandomSampling/Randomizer.cs index 1b3b44937..8c6c2feef 100644 --- a/src/NumSharp.Core/RandomSampling/Randomizer.cs +++ b/src/NumSharp.Core/RandomSampling/Randomizer.cs @@ -5,10 +5,9 @@ namespace NumSharp { /// /// Represents a pseudo-random number generator, which is a device that produces a sequence of numbers that meet certain statistical requirements for randomness.

- /// Equivalent of but with a . + /// Equivalent of . ///
/// Copied and modified from https://referencesource.microsoft.com/#mscorlib/system/random.cs - [Serializable] public sealed class Randomizer : ICloneable { private const int MBIG = int.MaxValue; diff --git a/src/NumSharp.Core/Utilities/ConcurrentHashset`1.cs b/src/NumSharp.Core/Utilities/ConcurrentHashset`1.cs index 0b7c1bfd1..5cee5f1b7 100644 --- a/src/NumSharp.Core/Utilities/ConcurrentHashset`1.cs +++ b/src/NumSharp.Core/Utilities/ConcurrentHashset`1.cs @@ -2,15 +2,13 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime.Serialization; using System.Threading; using NumSharp.Backends.Unmanaged; namespace NumSharp.Utilities { [DebuggerDisplay("Count = {Count}")] - [Serializable] - public class ConcurrentHashset : ICollection, ISet, ISerializable, IDeserializationCallback where T : unmanaged + public class ConcurrentHashset : ICollection, ISet where T : unmanaged { private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); @@ -34,15 +32,6 @@ public ConcurrentHashset(IEnumerable collection, IEqualityComparer compare hashset = new Hashset(collection, comparer); } - protected ConcurrentHashset(SerializationInfo info, StreamingContext context) - { - hashset = new Hashset(); - - // not sure about this one really... - var iSerializable = hashset as ISerializable; - iSerializable.GetObjectData(info, context); - } - #region Dispose public void Dispose() @@ -68,16 +57,6 @@ public IEnumerator GetEnumerator() Dispose(false); } - public void OnDeserialization(object sender) - { - hashset.OnDeserialization(sender); - } - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - hashset.GetObjectData(info, context); - } - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/src/NumSharp.Core/Utilities/Hashset`1.cs b/src/NumSharp.Core/Utilities/Hashset`1.cs index fc0e81d4b..ce221b492 100644 --- a/src/NumSharp.Core/Utilities/Hashset`1.cs +++ b/src/NumSharp.Core/Utilities/Hashset`1.cs @@ -5,9 +5,6 @@ using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; -using System.Runtime.Serialization; -using System.Security; -using System.Security.Permissions; using NumSharp.Backends.Unmanaged; namespace NumSharp.Utilities @@ -54,8 +51,7 @@ namespace NumSharp.Utilities /// [DebuggerDisplay("Count = {" + nameof(Count) + "}")] [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "By design")] - [Serializable()] - public class Hashset : ICollection, ISerializable, IDeserializationCallback, ISet, IReadOnlyCollection + public class Hashset : ICollection, ISet, IReadOnlyCollection { // store lower 31 bits of hash code private const int Lower31BitMask = 0x7FFFFFFF; @@ -70,14 +66,6 @@ public class Hashset : ICollection, ISerializable, IDeserializationCallbac // This is set to 3 because capacity is acceptable as 2x rounded up to nearest prime. private const int ShrinkThreshold = 3; -#if !SILVERLIGHT - // constants for serialization - private const String CapacityName = "Capacity"; - private const String ElementsName = "Elements"; - private const String ComparerName = "Comparer"; - private const String VersionName = "Version"; -#endif - private int[] m_buckets; private Slot[] m_slots; private int m_count; @@ -86,11 +74,6 @@ public class Hashset : ICollection, ISerializable, IDeserializationCallbac private IEqualityComparer m_comparer; private int m_version; -#if !SILVERLIGHT - // temporary variable needed during deserialization - private SerializationInfo m_siInfo; -#endif - #region Constructors public Hashset() @@ -204,17 +187,6 @@ private void CopyFrom(Hashset source) m_count = count; } -#if !SILVERLIGHT - protected Hashset(SerializationInfo info, StreamingContext context) - { - // We can't do anything with the keys and values until the entire graph has been - // deserialized and we have a reasonable estimate that GetHashCode is not going to - // fail. For the time being, we'll just cache this. The graph is not valid until - // OnDeserialization has been called. - m_siInfo = info; - } -#endif - public Hashset(int capacity, IEqualityComparer comparer) : this(comparer) { @@ -390,87 +362,6 @@ IEnumerator IEnumerable.GetEnumerator() #endregion - #region ISerializable methods - -#if !SILVERLIGHT - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] - [SecurityCritical] - public virtual void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } - - // need to serialize version to avoid problems with serializing while enumerating - info.AddValue(VersionName, m_version); - -#if FEATURE_RANDOMIZED_STRING_HASHING && !FEATURE_NETCORE - info.AddValue(ComparerName, HashHelpers.GetEqualityComparerForSerialization(m_comparer), typeof(IEqualityComparer)); -#else - info.AddValue(ComparerName, m_comparer, typeof(IEqualityComparer)); -#endif - - info.AddValue(CapacityName, m_buckets == null ? 0 : m_buckets.Length); - if (m_buckets != null) - { - T[] array = new T[m_count]; - CopyTo(array); - info.AddValue(ElementsName, array, typeof(T[])); - } - } -#endif - - #endregion - - #region IDeserializationCallback methods - -#if !SILVERLIGHT - public virtual void OnDeserialization(Object sender) - { - if (m_siInfo == null) - { - // It might be necessary to call OnDeserialization from a container if the - // container object also implements OnDeserialization. However, remoting will - // call OnDeserialization again. We can return immediately if this function is - // called twice. Note we set m_siInfo to null at the end of this method. - return; - } - - int capacity = m_siInfo.GetInt32(CapacityName); - m_comparer = (IEqualityComparer)m_siInfo.GetValue(ComparerName, typeof(IEqualityComparer)); - m_freeList = -1; - - if (capacity != 0) - { - m_buckets = new int[capacity]; - m_slots = new Slot[capacity]; - - T[] array = (T[])m_siInfo.GetValue(ElementsName, typeof(T[])); - - if (array == null) - { - throw new SerializationException("SR.GetString(SR.Serialization_MissingKeys)"); - } - - // there are no resizes here because we already set capacity above - for (int i = 0; i < array.Length; i++) - { - AddIfNotPresent(array[i]); - } - } - else - { - m_buckets = null; - } - - m_version = m_siInfo.GetInt32(VersionName); - m_siInfo = null; - } -#endif - - #endregion - #region HashSet methods /// @@ -1136,7 +1027,6 @@ public void TrimExcess() } } -#if !SILVERLIGHT || FEATURE_NETCORE /// /// Used for deep equality of HashSet testing /// @@ -1145,14 +1035,11 @@ public static IEqualityComparer> CreateSetComparer() { return new HashSetEqualityComparer(); } -#endif + /// /// Equality comparer for hashsets of hashsets /// /// -#if !FEATURE_NETCORE - [Serializable()] -#endif internal class HashSetEqualityComparer : IEqualityComparer> { private IEqualityComparer m_comparer; @@ -1306,18 +1193,12 @@ private bool AddIfNotPresent(T value) int hashCode = InternalGetHashCode(value); int bucket = hashCode % m_buckets.Length; -#if FEATURE_RANDOMIZED_STRING_HASHING && !FEATURE_NETCORE - int collisionCount = 0; -#endif for (int i = m_buckets[hashCode % m_buckets.Length] - 1; i >= 0; i = m_slots[i].next) { if (m_slots[i].hashCode == hashCode && m_comparer.Equals(m_slots[i].value, value)) { return false; } -#if FEATURE_RANDOMIZED_STRING_HASHING && !FEATURE_NETCORE - collisionCount++; -#endif } int index; @@ -1346,13 +1227,6 @@ private bool AddIfNotPresent(T value) m_count++; m_version++; -#if FEATURE_RANDOMIZED_STRING_HASHING && !FEATURE_NETCORE - if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(m_comparer)) { - m_comparer = (IEqualityComparer) HashHelpers.GetRandomizedEqualityComparer(m_comparer); - SetCapacity(m_buckets.Length, true); - } -#endif // FEATURE_RANDOMIZED_STRING_HASHING - return true; } @@ -1882,9 +1756,6 @@ internal struct Slot internal T value; } -#if !SILVERLIGHT - [Serializable()] -#endif public struct Enumerator : IEnumerator, IEnumerator { private Hashset set; diff --git a/src/NumSharp.Core/Utilities/NpzDictionary.cs b/src/NumSharp.Core/Utilities/NpzDictionary.cs index 0cc9575a6..f9bf2d3ec 100644 --- a/src/NumSharp.Core/Utilities/NpzDictionary.cs +++ b/src/NumSharp.Core/Utilities/NpzDictionary.cs @@ -11,9 +11,7 @@ namespace NumSharp { public class NpzDictionary : IDisposable, IReadOnlyDictionary, ICollection where T : class, -#if !NETSTANDARD1_4 ICloneable, -#endif IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { Stream stream; diff --git a/src/NumSharp.Core/Utilities/SteppingExtension.cs b/src/NumSharp.Core/Utilities/SteppingExtension.cs index 0c6744e95..38b01d4c0 100644 --- a/src/NumSharp.Core/Utilities/SteppingExtension.cs +++ b/src/NumSharp.Core/Utilities/SteppingExtension.cs @@ -16,8 +16,8 @@ public static T[] Step(this T[] array, int step) if (step == 1) return array; if (step == -1) - return array.Reverse().ToArray(); - var stepped_enumerable = Step(step < 0 ? array.Reverse().GetEnumerator() : array.OfType().GetEnumerator(), Math.Abs(step)); + return array.AsEnumerable().Reverse().ToArray(); + var stepped_enumerable = Step(step < 0 ? array.AsEnumerable().Reverse().GetEnumerator() : array.OfType().GetEnumerator(), Math.Abs(step)); return stepped_enumerable.ToArray(); } diff --git a/src/NumSharp.Core/Utilities/TypelessConvert.cs b/src/NumSharp.Core/Utilities/TypelessConvert.cs index ee70fac6b..b9ddb353a 100644 --- a/src/NumSharp.Core/Utilities/TypelessConvert.cs +++ b/src/NumSharp.Core/Utilities/TypelessConvert.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -12,175 +13,176 @@ namespace NumSharp.Utilities /// public static class TypelessConvert { - private static readonly Dictionary<(Type input, Type output), TypelessConvertDelegate> _delegates; + private static readonly FrozenDictionary<(Type input, Type output), TypelessConvertDelegate> _delegates; /// Initializes a new instance of the class. static TypelessConvert() { // ReSharper disable once UseObjectOrCollectionInitializer - _delegates = new Dictionary<(Type input, Type output), TypelessConvertDelegate>(); + var delegates = new Dictionary<(Type input, Type output), TypelessConvertDelegate>(); #if _REGEN %foreach forevery(supported_primitives, supported_primitives, true)% - _delegates.Add((typeof(#1), typeof(#2)), From#1To#2); + delegates.Add((typeof(#1), typeof(#2)), From#1To#2); % #else - _delegates.Add((typeof(Boolean), typeof(Byte)), FromBooleanToByte); - _delegates.Add((typeof(Boolean), typeof(Int16)), FromBooleanToInt16); - _delegates.Add((typeof(Boolean), typeof(UInt16)), FromBooleanToUInt16); - _delegates.Add((typeof(Boolean), typeof(Int32)), FromBooleanToInt32); - _delegates.Add((typeof(Boolean), typeof(UInt32)), FromBooleanToUInt32); - _delegates.Add((typeof(Boolean), typeof(Int64)), FromBooleanToInt64); - _delegates.Add((typeof(Boolean), typeof(UInt64)), FromBooleanToUInt64); - _delegates.Add((typeof(Boolean), typeof(Char)), FromBooleanToChar); - _delegates.Add((typeof(Boolean), typeof(Double)), FromBooleanToDouble); - _delegates.Add((typeof(Boolean), typeof(Single)), FromBooleanToSingle); - _delegates.Add((typeof(Boolean), typeof(Decimal)), FromBooleanToDecimal); - _delegates.Add((typeof(Boolean), typeof(String)), FromBooleanToString); - _delegates.Add((typeof(Byte), typeof(Boolean)), FromByteToBoolean); - _delegates.Add((typeof(Byte), typeof(Int16)), FromByteToInt16); - _delegates.Add((typeof(Byte), typeof(UInt16)), FromByteToUInt16); - _delegates.Add((typeof(Byte), typeof(Int32)), FromByteToInt32); - _delegates.Add((typeof(Byte), typeof(UInt32)), FromByteToUInt32); - _delegates.Add((typeof(Byte), typeof(Int64)), FromByteToInt64); - _delegates.Add((typeof(Byte), typeof(UInt64)), FromByteToUInt64); - _delegates.Add((typeof(Byte), typeof(Char)), FromByteToChar); - _delegates.Add((typeof(Byte), typeof(Double)), FromByteToDouble); - _delegates.Add((typeof(Byte), typeof(Single)), FromByteToSingle); - _delegates.Add((typeof(Byte), typeof(Decimal)), FromByteToDecimal); - _delegates.Add((typeof(Byte), typeof(String)), FromByteToString); - _delegates.Add((typeof(Int16), typeof(Boolean)), FromInt16ToBoolean); - _delegates.Add((typeof(Int16), typeof(Byte)), FromInt16ToByte); - _delegates.Add((typeof(Int16), typeof(UInt16)), FromInt16ToUInt16); - _delegates.Add((typeof(Int16), typeof(Int32)), FromInt16ToInt32); - _delegates.Add((typeof(Int16), typeof(UInt32)), FromInt16ToUInt32); - _delegates.Add((typeof(Int16), typeof(Int64)), FromInt16ToInt64); - _delegates.Add((typeof(Int16), typeof(UInt64)), FromInt16ToUInt64); - _delegates.Add((typeof(Int16), typeof(Char)), FromInt16ToChar); - _delegates.Add((typeof(Int16), typeof(Double)), FromInt16ToDouble); - _delegates.Add((typeof(Int16), typeof(Single)), FromInt16ToSingle); - _delegates.Add((typeof(Int16), typeof(Decimal)), FromInt16ToDecimal); - _delegates.Add((typeof(Int16), typeof(String)), FromInt16ToString); - _delegates.Add((typeof(UInt16), typeof(Boolean)), FromUInt16ToBoolean); - _delegates.Add((typeof(UInt16), typeof(Byte)), FromUInt16ToByte); - _delegates.Add((typeof(UInt16), typeof(Int16)), FromUInt16ToInt16); - _delegates.Add((typeof(UInt16), typeof(Int32)), FromUInt16ToInt32); - _delegates.Add((typeof(UInt16), typeof(UInt32)), FromUInt16ToUInt32); - _delegates.Add((typeof(UInt16), typeof(Int64)), FromUInt16ToInt64); - _delegates.Add((typeof(UInt16), typeof(UInt64)), FromUInt16ToUInt64); - _delegates.Add((typeof(UInt16), typeof(Char)), FromUInt16ToChar); - _delegates.Add((typeof(UInt16), typeof(Double)), FromUInt16ToDouble); - _delegates.Add((typeof(UInt16), typeof(Single)), FromUInt16ToSingle); - _delegates.Add((typeof(UInt16), typeof(Decimal)), FromUInt16ToDecimal); - _delegates.Add((typeof(UInt16), typeof(String)), FromUInt16ToString); - _delegates.Add((typeof(Int32), typeof(Boolean)), FromInt32ToBoolean); - _delegates.Add((typeof(Int32), typeof(Byte)), FromInt32ToByte); - _delegates.Add((typeof(Int32), typeof(Int16)), FromInt32ToInt16); - _delegates.Add((typeof(Int32), typeof(UInt16)), FromInt32ToUInt16); - _delegates.Add((typeof(Int32), typeof(UInt32)), FromInt32ToUInt32); - _delegates.Add((typeof(Int32), typeof(Int64)), FromInt32ToInt64); - _delegates.Add((typeof(Int32), typeof(UInt64)), FromInt32ToUInt64); - _delegates.Add((typeof(Int32), typeof(Char)), FromInt32ToChar); - _delegates.Add((typeof(Int32), typeof(Double)), FromInt32ToDouble); - _delegates.Add((typeof(Int32), typeof(Single)), FromInt32ToSingle); - _delegates.Add((typeof(Int32), typeof(Decimal)), FromInt32ToDecimal); - _delegates.Add((typeof(Int32), typeof(String)), FromInt32ToString); - _delegates.Add((typeof(UInt32), typeof(Boolean)), FromUInt32ToBoolean); - _delegates.Add((typeof(UInt32), typeof(Byte)), FromUInt32ToByte); - _delegates.Add((typeof(UInt32), typeof(Int16)), FromUInt32ToInt16); - _delegates.Add((typeof(UInt32), typeof(UInt16)), FromUInt32ToUInt16); - _delegates.Add((typeof(UInt32), typeof(Int32)), FromUInt32ToInt32); - _delegates.Add((typeof(UInt32), typeof(Int64)), FromUInt32ToInt64); - _delegates.Add((typeof(UInt32), typeof(UInt64)), FromUInt32ToUInt64); - _delegates.Add((typeof(UInt32), typeof(Char)), FromUInt32ToChar); - _delegates.Add((typeof(UInt32), typeof(Double)), FromUInt32ToDouble); - _delegates.Add((typeof(UInt32), typeof(Single)), FromUInt32ToSingle); - _delegates.Add((typeof(UInt32), typeof(Decimal)), FromUInt32ToDecimal); - _delegates.Add((typeof(UInt32), typeof(String)), FromUInt32ToString); - _delegates.Add((typeof(Int64), typeof(Boolean)), FromInt64ToBoolean); - _delegates.Add((typeof(Int64), typeof(Byte)), FromInt64ToByte); - _delegates.Add((typeof(Int64), typeof(Int16)), FromInt64ToInt16); - _delegates.Add((typeof(Int64), typeof(UInt16)), FromInt64ToUInt16); - _delegates.Add((typeof(Int64), typeof(Int32)), FromInt64ToInt32); - _delegates.Add((typeof(Int64), typeof(UInt32)), FromInt64ToUInt32); - _delegates.Add((typeof(Int64), typeof(UInt64)), FromInt64ToUInt64); - _delegates.Add((typeof(Int64), typeof(Char)), FromInt64ToChar); - _delegates.Add((typeof(Int64), typeof(Double)), FromInt64ToDouble); - _delegates.Add((typeof(Int64), typeof(Single)), FromInt64ToSingle); - _delegates.Add((typeof(Int64), typeof(Decimal)), FromInt64ToDecimal); - _delegates.Add((typeof(Int64), typeof(String)), FromInt64ToString); - _delegates.Add((typeof(UInt64), typeof(Boolean)), FromUInt64ToBoolean); - _delegates.Add((typeof(UInt64), typeof(Byte)), FromUInt64ToByte); - _delegates.Add((typeof(UInt64), typeof(Int16)), FromUInt64ToInt16); - _delegates.Add((typeof(UInt64), typeof(UInt16)), FromUInt64ToUInt16); - _delegates.Add((typeof(UInt64), typeof(Int32)), FromUInt64ToInt32); - _delegates.Add((typeof(UInt64), typeof(UInt32)), FromUInt64ToUInt32); - _delegates.Add((typeof(UInt64), typeof(Int64)), FromUInt64ToInt64); - _delegates.Add((typeof(UInt64), typeof(Char)), FromUInt64ToChar); - _delegates.Add((typeof(UInt64), typeof(Double)), FromUInt64ToDouble); - _delegates.Add((typeof(UInt64), typeof(Single)), FromUInt64ToSingle); - _delegates.Add((typeof(UInt64), typeof(Decimal)), FromUInt64ToDecimal); - _delegates.Add((typeof(UInt64), typeof(String)), FromUInt64ToString); - _delegates.Add((typeof(Char), typeof(Boolean)), FromCharToBoolean); - _delegates.Add((typeof(Char), typeof(Byte)), FromCharToByte); - _delegates.Add((typeof(Char), typeof(Int16)), FromCharToInt16); - _delegates.Add((typeof(Char), typeof(UInt16)), FromCharToUInt16); - _delegates.Add((typeof(Char), typeof(Int32)), FromCharToInt32); - _delegates.Add((typeof(Char), typeof(UInt32)), FromCharToUInt32); - _delegates.Add((typeof(Char), typeof(Int64)), FromCharToInt64); - _delegates.Add((typeof(Char), typeof(UInt64)), FromCharToUInt64); - _delegates.Add((typeof(Char), typeof(Double)), FromCharToDouble); - _delegates.Add((typeof(Char), typeof(Single)), FromCharToSingle); - _delegates.Add((typeof(Char), typeof(Decimal)), FromCharToDecimal); - _delegates.Add((typeof(Char), typeof(String)), FromCharToString); - _delegates.Add((typeof(Double), typeof(Boolean)), FromDoubleToBoolean); - _delegates.Add((typeof(Double), typeof(Byte)), FromDoubleToByte); - _delegates.Add((typeof(Double), typeof(Int16)), FromDoubleToInt16); - _delegates.Add((typeof(Double), typeof(UInt16)), FromDoubleToUInt16); - _delegates.Add((typeof(Double), typeof(Int32)), FromDoubleToInt32); - _delegates.Add((typeof(Double), typeof(UInt32)), FromDoubleToUInt32); - _delegates.Add((typeof(Double), typeof(Int64)), FromDoubleToInt64); - _delegates.Add((typeof(Double), typeof(UInt64)), FromDoubleToUInt64); - _delegates.Add((typeof(Double), typeof(Char)), FromDoubleToChar); - _delegates.Add((typeof(Double), typeof(Single)), FromDoubleToSingle); - _delegates.Add((typeof(Double), typeof(Decimal)), FromDoubleToDecimal); - _delegates.Add((typeof(Double), typeof(String)), FromDoubleToString); - _delegates.Add((typeof(Single), typeof(Boolean)), FromSingleToBoolean); - _delegates.Add((typeof(Single), typeof(Byte)), FromSingleToByte); - _delegates.Add((typeof(Single), typeof(Int16)), FromSingleToInt16); - _delegates.Add((typeof(Single), typeof(UInt16)), FromSingleToUInt16); - _delegates.Add((typeof(Single), typeof(Int32)), FromSingleToInt32); - _delegates.Add((typeof(Single), typeof(UInt32)), FromSingleToUInt32); - _delegates.Add((typeof(Single), typeof(Int64)), FromSingleToInt64); - _delegates.Add((typeof(Single), typeof(UInt64)), FromSingleToUInt64); - _delegates.Add((typeof(Single), typeof(Char)), FromSingleToChar); - _delegates.Add((typeof(Single), typeof(Double)), FromSingleToDouble); - _delegates.Add((typeof(Single), typeof(Decimal)), FromSingleToDecimal); - _delegates.Add((typeof(Single), typeof(String)), FromSingleToString); - _delegates.Add((typeof(Decimal), typeof(Boolean)), FromDecimalToBoolean); - _delegates.Add((typeof(Decimal), typeof(Byte)), FromDecimalToByte); - _delegates.Add((typeof(Decimal), typeof(Int16)), FromDecimalToInt16); - _delegates.Add((typeof(Decimal), typeof(UInt16)), FromDecimalToUInt16); - _delegates.Add((typeof(Decimal), typeof(Int32)), FromDecimalToInt32); - _delegates.Add((typeof(Decimal), typeof(UInt32)), FromDecimalToUInt32); - _delegates.Add((typeof(Decimal), typeof(Int64)), FromDecimalToInt64); - _delegates.Add((typeof(Decimal), typeof(UInt64)), FromDecimalToUInt64); - _delegates.Add((typeof(Decimal), typeof(Char)), FromDecimalToChar); - _delegates.Add((typeof(Decimal), typeof(Double)), FromDecimalToDouble); - _delegates.Add((typeof(Decimal), typeof(Single)), FromDecimalToSingle); - _delegates.Add((typeof(Decimal), typeof(String)), FromDecimalToString); - _delegates.Add((typeof(String), typeof(Boolean)), FromStringToBoolean); - _delegates.Add((typeof(String), typeof(Byte)), FromStringToByte); - _delegates.Add((typeof(String), typeof(Int16)), FromStringToInt16); - _delegates.Add((typeof(String), typeof(UInt16)), FromStringToUInt16); - _delegates.Add((typeof(String), typeof(Int32)), FromStringToInt32); - _delegates.Add((typeof(String), typeof(UInt32)), FromStringToUInt32); - _delegates.Add((typeof(String), typeof(Int64)), FromStringToInt64); - _delegates.Add((typeof(String), typeof(UInt64)), FromStringToUInt64); - _delegates.Add((typeof(String), typeof(Char)), FromStringToChar); - _delegates.Add((typeof(String), typeof(Double)), FromStringToDouble); - _delegates.Add((typeof(String), typeof(Single)), FromStringToSingle); - _delegates.Add((typeof(String), typeof(Decimal)), FromStringToDecimal); + delegates.Add((typeof(Boolean), typeof(Byte)), FromBooleanToByte); + delegates.Add((typeof(Boolean), typeof(Int16)), FromBooleanToInt16); + delegates.Add((typeof(Boolean), typeof(UInt16)), FromBooleanToUInt16); + delegates.Add((typeof(Boolean), typeof(Int32)), FromBooleanToInt32); + delegates.Add((typeof(Boolean), typeof(UInt32)), FromBooleanToUInt32); + delegates.Add((typeof(Boolean), typeof(Int64)), FromBooleanToInt64); + delegates.Add((typeof(Boolean), typeof(UInt64)), FromBooleanToUInt64); + delegates.Add((typeof(Boolean), typeof(Char)), FromBooleanToChar); + delegates.Add((typeof(Boolean), typeof(Double)), FromBooleanToDouble); + delegates.Add((typeof(Boolean), typeof(Single)), FromBooleanToSingle); + delegates.Add((typeof(Boolean), typeof(Decimal)), FromBooleanToDecimal); + delegates.Add((typeof(Boolean), typeof(String)), FromBooleanToString); + delegates.Add((typeof(Byte), typeof(Boolean)), FromByteToBoolean); + delegates.Add((typeof(Byte), typeof(Int16)), FromByteToInt16); + delegates.Add((typeof(Byte), typeof(UInt16)), FromByteToUInt16); + delegates.Add((typeof(Byte), typeof(Int32)), FromByteToInt32); + delegates.Add((typeof(Byte), typeof(UInt32)), FromByteToUInt32); + delegates.Add((typeof(Byte), typeof(Int64)), FromByteToInt64); + delegates.Add((typeof(Byte), typeof(UInt64)), FromByteToUInt64); + delegates.Add((typeof(Byte), typeof(Char)), FromByteToChar); + delegates.Add((typeof(Byte), typeof(Double)), FromByteToDouble); + delegates.Add((typeof(Byte), typeof(Single)), FromByteToSingle); + delegates.Add((typeof(Byte), typeof(Decimal)), FromByteToDecimal); + delegates.Add((typeof(Byte), typeof(String)), FromByteToString); + delegates.Add((typeof(Int16), typeof(Boolean)), FromInt16ToBoolean); + delegates.Add((typeof(Int16), typeof(Byte)), FromInt16ToByte); + delegates.Add((typeof(Int16), typeof(UInt16)), FromInt16ToUInt16); + delegates.Add((typeof(Int16), typeof(Int32)), FromInt16ToInt32); + delegates.Add((typeof(Int16), typeof(UInt32)), FromInt16ToUInt32); + delegates.Add((typeof(Int16), typeof(Int64)), FromInt16ToInt64); + delegates.Add((typeof(Int16), typeof(UInt64)), FromInt16ToUInt64); + delegates.Add((typeof(Int16), typeof(Char)), FromInt16ToChar); + delegates.Add((typeof(Int16), typeof(Double)), FromInt16ToDouble); + delegates.Add((typeof(Int16), typeof(Single)), FromInt16ToSingle); + delegates.Add((typeof(Int16), typeof(Decimal)), FromInt16ToDecimal); + delegates.Add((typeof(Int16), typeof(String)), FromInt16ToString); + delegates.Add((typeof(UInt16), typeof(Boolean)), FromUInt16ToBoolean); + delegates.Add((typeof(UInt16), typeof(Byte)), FromUInt16ToByte); + delegates.Add((typeof(UInt16), typeof(Int16)), FromUInt16ToInt16); + delegates.Add((typeof(UInt16), typeof(Int32)), FromUInt16ToInt32); + delegates.Add((typeof(UInt16), typeof(UInt32)), FromUInt16ToUInt32); + delegates.Add((typeof(UInt16), typeof(Int64)), FromUInt16ToInt64); + delegates.Add((typeof(UInt16), typeof(UInt64)), FromUInt16ToUInt64); + delegates.Add((typeof(UInt16), typeof(Char)), FromUInt16ToChar); + delegates.Add((typeof(UInt16), typeof(Double)), FromUInt16ToDouble); + delegates.Add((typeof(UInt16), typeof(Single)), FromUInt16ToSingle); + delegates.Add((typeof(UInt16), typeof(Decimal)), FromUInt16ToDecimal); + delegates.Add((typeof(UInt16), typeof(String)), FromUInt16ToString); + delegates.Add((typeof(Int32), typeof(Boolean)), FromInt32ToBoolean); + delegates.Add((typeof(Int32), typeof(Byte)), FromInt32ToByte); + delegates.Add((typeof(Int32), typeof(Int16)), FromInt32ToInt16); + delegates.Add((typeof(Int32), typeof(UInt16)), FromInt32ToUInt16); + delegates.Add((typeof(Int32), typeof(UInt32)), FromInt32ToUInt32); + delegates.Add((typeof(Int32), typeof(Int64)), FromInt32ToInt64); + delegates.Add((typeof(Int32), typeof(UInt64)), FromInt32ToUInt64); + delegates.Add((typeof(Int32), typeof(Char)), FromInt32ToChar); + delegates.Add((typeof(Int32), typeof(Double)), FromInt32ToDouble); + delegates.Add((typeof(Int32), typeof(Single)), FromInt32ToSingle); + delegates.Add((typeof(Int32), typeof(Decimal)), FromInt32ToDecimal); + delegates.Add((typeof(Int32), typeof(String)), FromInt32ToString); + delegates.Add((typeof(UInt32), typeof(Boolean)), FromUInt32ToBoolean); + delegates.Add((typeof(UInt32), typeof(Byte)), FromUInt32ToByte); + delegates.Add((typeof(UInt32), typeof(Int16)), FromUInt32ToInt16); + delegates.Add((typeof(UInt32), typeof(UInt16)), FromUInt32ToUInt16); + delegates.Add((typeof(UInt32), typeof(Int32)), FromUInt32ToInt32); + delegates.Add((typeof(UInt32), typeof(Int64)), FromUInt32ToInt64); + delegates.Add((typeof(UInt32), typeof(UInt64)), FromUInt32ToUInt64); + delegates.Add((typeof(UInt32), typeof(Char)), FromUInt32ToChar); + delegates.Add((typeof(UInt32), typeof(Double)), FromUInt32ToDouble); + delegates.Add((typeof(UInt32), typeof(Single)), FromUInt32ToSingle); + delegates.Add((typeof(UInt32), typeof(Decimal)), FromUInt32ToDecimal); + delegates.Add((typeof(UInt32), typeof(String)), FromUInt32ToString); + delegates.Add((typeof(Int64), typeof(Boolean)), FromInt64ToBoolean); + delegates.Add((typeof(Int64), typeof(Byte)), FromInt64ToByte); + delegates.Add((typeof(Int64), typeof(Int16)), FromInt64ToInt16); + delegates.Add((typeof(Int64), typeof(UInt16)), FromInt64ToUInt16); + delegates.Add((typeof(Int64), typeof(Int32)), FromInt64ToInt32); + delegates.Add((typeof(Int64), typeof(UInt32)), FromInt64ToUInt32); + delegates.Add((typeof(Int64), typeof(UInt64)), FromInt64ToUInt64); + delegates.Add((typeof(Int64), typeof(Char)), FromInt64ToChar); + delegates.Add((typeof(Int64), typeof(Double)), FromInt64ToDouble); + delegates.Add((typeof(Int64), typeof(Single)), FromInt64ToSingle); + delegates.Add((typeof(Int64), typeof(Decimal)), FromInt64ToDecimal); + delegates.Add((typeof(Int64), typeof(String)), FromInt64ToString); + delegates.Add((typeof(UInt64), typeof(Boolean)), FromUInt64ToBoolean); + delegates.Add((typeof(UInt64), typeof(Byte)), FromUInt64ToByte); + delegates.Add((typeof(UInt64), typeof(Int16)), FromUInt64ToInt16); + delegates.Add((typeof(UInt64), typeof(UInt16)), FromUInt64ToUInt16); + delegates.Add((typeof(UInt64), typeof(Int32)), FromUInt64ToInt32); + delegates.Add((typeof(UInt64), typeof(UInt32)), FromUInt64ToUInt32); + delegates.Add((typeof(UInt64), typeof(Int64)), FromUInt64ToInt64); + delegates.Add((typeof(UInt64), typeof(Char)), FromUInt64ToChar); + delegates.Add((typeof(UInt64), typeof(Double)), FromUInt64ToDouble); + delegates.Add((typeof(UInt64), typeof(Single)), FromUInt64ToSingle); + delegates.Add((typeof(UInt64), typeof(Decimal)), FromUInt64ToDecimal); + delegates.Add((typeof(UInt64), typeof(String)), FromUInt64ToString); + delegates.Add((typeof(Char), typeof(Boolean)), FromCharToBoolean); + delegates.Add((typeof(Char), typeof(Byte)), FromCharToByte); + delegates.Add((typeof(Char), typeof(Int16)), FromCharToInt16); + delegates.Add((typeof(Char), typeof(UInt16)), FromCharToUInt16); + delegates.Add((typeof(Char), typeof(Int32)), FromCharToInt32); + delegates.Add((typeof(Char), typeof(UInt32)), FromCharToUInt32); + delegates.Add((typeof(Char), typeof(Int64)), FromCharToInt64); + delegates.Add((typeof(Char), typeof(UInt64)), FromCharToUInt64); + delegates.Add((typeof(Char), typeof(Double)), FromCharToDouble); + delegates.Add((typeof(Char), typeof(Single)), FromCharToSingle); + delegates.Add((typeof(Char), typeof(Decimal)), FromCharToDecimal); + delegates.Add((typeof(Char), typeof(String)), FromCharToString); + delegates.Add((typeof(Double), typeof(Boolean)), FromDoubleToBoolean); + delegates.Add((typeof(Double), typeof(Byte)), FromDoubleToByte); + delegates.Add((typeof(Double), typeof(Int16)), FromDoubleToInt16); + delegates.Add((typeof(Double), typeof(UInt16)), FromDoubleToUInt16); + delegates.Add((typeof(Double), typeof(Int32)), FromDoubleToInt32); + delegates.Add((typeof(Double), typeof(UInt32)), FromDoubleToUInt32); + delegates.Add((typeof(Double), typeof(Int64)), FromDoubleToInt64); + delegates.Add((typeof(Double), typeof(UInt64)), FromDoubleToUInt64); + delegates.Add((typeof(Double), typeof(Char)), FromDoubleToChar); + delegates.Add((typeof(Double), typeof(Single)), FromDoubleToSingle); + delegates.Add((typeof(Double), typeof(Decimal)), FromDoubleToDecimal); + delegates.Add((typeof(Double), typeof(String)), FromDoubleToString); + delegates.Add((typeof(Single), typeof(Boolean)), FromSingleToBoolean); + delegates.Add((typeof(Single), typeof(Byte)), FromSingleToByte); + delegates.Add((typeof(Single), typeof(Int16)), FromSingleToInt16); + delegates.Add((typeof(Single), typeof(UInt16)), FromSingleToUInt16); + delegates.Add((typeof(Single), typeof(Int32)), FromSingleToInt32); + delegates.Add((typeof(Single), typeof(UInt32)), FromSingleToUInt32); + delegates.Add((typeof(Single), typeof(Int64)), FromSingleToInt64); + delegates.Add((typeof(Single), typeof(UInt64)), FromSingleToUInt64); + delegates.Add((typeof(Single), typeof(Char)), FromSingleToChar); + delegates.Add((typeof(Single), typeof(Double)), FromSingleToDouble); + delegates.Add((typeof(Single), typeof(Decimal)), FromSingleToDecimal); + delegates.Add((typeof(Single), typeof(String)), FromSingleToString); + delegates.Add((typeof(Decimal), typeof(Boolean)), FromDecimalToBoolean); + delegates.Add((typeof(Decimal), typeof(Byte)), FromDecimalToByte); + delegates.Add((typeof(Decimal), typeof(Int16)), FromDecimalToInt16); + delegates.Add((typeof(Decimal), typeof(UInt16)), FromDecimalToUInt16); + delegates.Add((typeof(Decimal), typeof(Int32)), FromDecimalToInt32); + delegates.Add((typeof(Decimal), typeof(UInt32)), FromDecimalToUInt32); + delegates.Add((typeof(Decimal), typeof(Int64)), FromDecimalToInt64); + delegates.Add((typeof(Decimal), typeof(UInt64)), FromDecimalToUInt64); + delegates.Add((typeof(Decimal), typeof(Char)), FromDecimalToChar); + delegates.Add((typeof(Decimal), typeof(Double)), FromDecimalToDouble); + delegates.Add((typeof(Decimal), typeof(Single)), FromDecimalToSingle); + delegates.Add((typeof(Decimal), typeof(String)), FromDecimalToString); + delegates.Add((typeof(String), typeof(Boolean)), FromStringToBoolean); + delegates.Add((typeof(String), typeof(Byte)), FromStringToByte); + delegates.Add((typeof(String), typeof(Int16)), FromStringToInt16); + delegates.Add((typeof(String), typeof(UInt16)), FromStringToUInt16); + delegates.Add((typeof(String), typeof(Int32)), FromStringToInt32); + delegates.Add((typeof(String), typeof(UInt32)), FromStringToUInt32); + delegates.Add((typeof(String), typeof(Int64)), FromStringToInt64); + delegates.Add((typeof(String), typeof(UInt64)), FromStringToUInt64); + delegates.Add((typeof(String), typeof(Char)), FromStringToChar); + delegates.Add((typeof(String), typeof(Double)), FromStringToDouble); + delegates.Add((typeof(String), typeof(Single)), FromStringToSingle); + delegates.Add((typeof(String), typeof(Decimal)), FromStringToDecimal); #endif + _delegates = delegates.ToFrozenDictionary(); } public static TypelessConvertDelegate GetConverter(Type input, Type output) diff --git a/src/numpy b/src/numpy new file mode 160000 index 000000000..c81c49f77 --- /dev/null +++ b/src/numpy @@ -0,0 +1 @@ +Subproject commit c81c49f77451340651a751e76bca607d85e4fd55 diff --git a/test/NumSharp.Benchmark/NumSharp.Benchmark.csproj b/test/NumSharp.Benchmark/NumSharp.Benchmark.csproj index 7cbdd3353..af1bdf413 100644 --- a/test/NumSharp.Benchmark/NumSharp.Benchmark.csproj +++ b/test/NumSharp.Benchmark/NumSharp.Benchmark.csproj @@ -2,8 +2,8 @@ Exe - net8.0 - 12.0 + net8.0;net10.0 + latest AnyCPU;x64 @@ -18,8 +18,7 @@ - - +
diff --git a/test/NumSharp.ConsumePackage/NumSharp.ConsumePackage.csproj b/test/NumSharp.ConsumePackage/NumSharp.ConsumePackage.csproj index dab21ff5f..014f80521 100644 --- a/test/NumSharp.ConsumePackage/NumSharp.ConsumePackage.csproj +++ b/test/NumSharp.ConsumePackage/NumSharp.ConsumePackage.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.1 + net8.0;net10.0 diff --git a/test/NumSharp.Examples/NumSharp.Examples.csproj b/test/NumSharp.Examples/NumSharp.Examples.csproj index 478a03fc2..03c0317cd 100644 --- a/test/NumSharp.Examples/NumSharp.Examples.csproj +++ b/test/NumSharp.Examples/NumSharp.Examples.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + net8.0;net10.0 diff --git a/test/NumSharp.Examples/ShallowWater.cs b/test/NumSharp.Examples/ShallowWater.cs index 6a94b11c3..55f2c2ab0 100644 --- a/test/NumSharp.Examples/ShallowWater.cs +++ b/test/NumSharp.Examples/ShallowWater.cs @@ -1,5 +1,5 @@ using System; -using NumSharp.Core; +using NumSharp; using System.Collections; using System.Collections.Generic; diff --git a/test/NumSharp.UnitTest/Creation/NpBroadcastFromNumPyTests.cs b/test/NumSharp.UnitTest/Creation/NpBroadcastFromNumPyTests.cs new file mode 100644 index 000000000..c277a6e5f --- /dev/null +++ b/test/NumSharp.UnitTest/Creation/NpBroadcastFromNumPyTests.cs @@ -0,0 +1,1877 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Linq; +using FluentAssertions; +using NumSharp; +using NumSharp.Backends; + +namespace NumSharp.UnitTest.Creation +{ + /// + /// Comprehensive broadcast tests ported from NumPy's test suite. + /// Sources: + /// - numpy/lib/tests/test_stride_tricks.py (broadcast_to, broadcast_arrays, broadcast_shapes) + /// - numpy/_core/tests/test_numeric.py (np.broadcast class) + /// Verifies NumSharp's broadcasting against NumPy 2.x behavior. + /// + [TestClass] + public class NpBroadcastFromNumPyTests : TestClass + { + #region Helpers + + /// + /// Creates an NDArray filled with ones of the given shape. + /// For zero-size shapes, returns an empty array. + /// + private static NDArray ones(params int[] shape) + { + return np.ones(new Shape(shape)); + } + + /// + /// Assert that two shapes are identical. + /// + private static void AssertShapeEqual(Shape actual, params int[] expected) + { + actual.dimensions.Should().BeEquivalentTo(expected, + $"expected shape ({string.Join(",", expected)}) but got ({string.Join(",", actual.dimensions)})"); + } + + /// + /// Assert that an NDArray has a given shape. + /// + private static void AssertShapeEqual(NDArray actual, params int[] expected) + { + AssertShapeEqual(actual.Shape, expected); + } + + #endregion + + // ================================================================ + // Group 1: broadcast_arrays Shape Resolution + // (from numpy/lib/tests/test_stride_tricks.py) + // ================================================================ + + #region Group 1: broadcast_arrays Shape Resolution + + /// + /// Two identical (1,3) arrays broadcast unchanged. + /// >>> x = np.array([[1, 2, 3]]) + /// >>> a, b = np.broadcast_arrays(x, x) + /// >>> np.array_equal(a, x) and np.array_equal(b, x) + /// True + /// + [TestMethod] + public void BroadcastArrays_Same() + { + var x = np.array(new int[,] { { 1, 2, 3 } }); // shape (1,3) + var (a, b) = np.broadcast_arrays(x, x); + + AssertShapeEqual(a, 1, 3); + AssertShapeEqual(b, 1, 3); + np.array_equal(a, x).Should().BeTrue(); + np.array_equal(b, x).Should().BeTrue(); + } + + /// + /// (1,3) + (3,1) -> both become (3,3) with correct values. + /// >>> x = np.array([[1, 2, 3]]) # shape (1,3) + /// >>> y = np.array([[1], [2], [3]]) # shape (3,1) + /// >>> a, b = np.broadcast_arrays(x, y) + /// >>> a + /// array([[1, 2, 3], + /// [1, 2, 3], + /// [1, 2, 3]]) + /// >>> b + /// array([[1, 1, 1], + /// [2, 2, 2], + /// [3, 3, 3]]) + /// + [TestMethod] + public void BroadcastArrays_OneOff() + { + var x = np.array(new int[,] { { 1, 2, 3 } }); // shape (1,3) + var y = np.array(new int[,] { { 1 }, { 2 }, { 3 } }); // shape (3,1) + var (a, b) = np.broadcast_arrays(x, y); + + AssertShapeEqual(a, 3, 3); + AssertShapeEqual(b, 3, 3); + + // a should be [[1,2,3],[1,2,3],[1,2,3]] + var expected_a = np.array(new int[,] { { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 } }); + np.array_equal(a, expected_a).Should().BeTrue(); + + // b should be [[1,1,1],[2,2,2],[3,3,3]] + var expected_b = np.array(new int[,] { { 1, 1, 1 }, { 2, 2, 2 }, { 3, 3, 3 } }); + np.array_equal(b, expected_b).Should().BeTrue(); + } + + /// + /// Comprehensive same-input-shapes test from test_stride_tricks.py. + /// For each shape in a set of shapes, broadcasting an array with itself + /// should produce the same shape. Testing with 1, 2, and 3 identical inputs. + /// + /// Shapes tested: (1,), (3,), (1,3), (3,1), (3,3) + /// (Omitting zero-size shapes since they require special handling) + /// + [TestMethod] + public void BroadcastArrays_SameInputShapes() + { + var shapes = new[] + { + new int[] { 1 }, + new int[] { 3 }, + new int[] { 1, 3 }, + new int[] { 3, 1 }, + new int[] { 3, 3 }, + }; + + foreach (var shape in shapes) + { + var x = np.ones(new Shape(shape)); + + // Single broadcast (two identical) + var (a, b) = np.broadcast_arrays(x, x); + AssertShapeEqual(a, shape); + AssertShapeEqual(b, shape); + + // Triple broadcast (three identical) + var result = np.broadcast_arrays(x, x, x); + result.Length.Should().Be(3); + foreach (var r in result) + AssertShapeEqual(r, shape); + } + } + + /// + /// Compatible shape pairs where one dimension is 1. + /// From test_stride_tricks.py _test_broadcast_compatible_by_ones. + /// + /// Tests both (a,b) and (b,a) orderings for symmetry. + /// + [TestMethod] + public void BroadcastArrays_CompatibleByOnes() + { + // (input1_shape, input2_shape, expected_output_shape) + var cases = new[] + { + (new int[] { 1 }, new int[] { 3 }, new int[] { 3 }), + (new int[] { 1, 3 }, new int[] { 3, 3 }, new int[] { 3, 3 }), + (new int[] { 3, 1 }, new int[] { 3, 3 }, new int[] { 3, 3 }), + (new int[] { 1, 3 }, new int[] { 3, 1 }, new int[] { 3, 3 }), + (new int[] { 1, 1 }, new int[] { 3, 3 }, new int[] { 3, 3 }), + (new int[] { 1, 1 }, new int[] { 1, 3 }, new int[] { 1, 3 }), + (new int[] { 1, 1 }, new int[] { 3, 1 }, new int[] { 3, 1 }), + (new int[] { 1, 3 }, new int[] { 1, 3 }, new int[] { 1, 3 }), + (new int[] { 3, 1 }, new int[] { 3, 1 }, new int[] { 3, 1 }), + (new int[] { 3, 3 }, new int[] { 3, 3 }, new int[] { 3, 3 }), + // Higher dimensional + (new int[] { 1, 1, 1 }, new int[] { 3, 3, 3 }, new int[] { 3, 3, 3 }), + (new int[] { 1, 3, 1 }, new int[] { 3, 1, 3 }, new int[] { 3, 3, 3 }), + (new int[] { 3, 1, 1 }, new int[] { 1, 3, 3 }, new int[] { 3, 3, 3 }), + }; + + foreach (var (s1, s2, expected) in cases) + { + // Forward order + var x1 = np.ones(new Shape(s1)); + var x2 = np.ones(new Shape(s2)); + var (a, b) = np.broadcast_arrays(x1, x2); + AssertShapeEqual(a, expected); + AssertShapeEqual(b, expected); + + // Reversed order (symmetry) + var (a2, b2) = np.broadcast_arrays(x2, x1); + AssertShapeEqual(a2, expected); + AssertShapeEqual(b2, expected); + } + } + + /// + /// Compatible shape pairs with different ndim (prepending ones). + /// From test_stride_tricks.py _test_broadcast_compatible_by_prepending_ones. + /// + /// Tests both (a,b) and (b,a) orderings for symmetry. + /// + [TestMethod] + public void BroadcastArrays_CompatibleByPrependingOnes() + { + // (input1_shape, input2_shape, expected_output_shape) + var cases = new[] + { + (new int[] { 3 }, new int[] { 3, 3 }, new int[] { 3, 3 }), + (new int[] { 3 }, new int[] { 3, 1 }, new int[] { 3, 3 }), + (new int[] { 1 }, new int[] { 3, 3 }, new int[] { 3, 3 }), + (new int[] { 1 }, new int[] { 1, 3 }, new int[] { 1, 3 }), + (new int[] { 1 }, new int[] { 3, 1 }, new int[] { 3, 1 }), + (new int[] { 3 }, new int[] { 1, 3 }, new int[] { 1, 3 }), + (new int[] { 1 }, new int[] { 1, 1 }, new int[] { 1, 1 }), + (new int[] { 3 }, new int[] { 3, 3, 3 }, new int[] { 3, 3, 3 }), + (new int[] { 1 }, new int[] { 3, 3, 3 }, new int[] { 3, 3, 3 }), + (new int[] { 1, 3 }, new int[] { 3, 3, 3 }, new int[] { 3, 3, 3 }), + (new int[] { 3, 3 }, new int[] { 3, 3, 3 }, new int[] { 3, 3, 3 }), + (new int[] { 1, 1 }, new int[] { 3, 3, 3 }, new int[] { 3, 3, 3 }), + (new int[] { 1, 3 }, new int[] { 3, 1, 3 }, new int[] { 3, 1, 3 }), + (new int[] { 3, 1 }, new int[] { 3, 3, 1 }, new int[] { 3, 3, 1 }), + }; + + foreach (var (s1, s2, expected) in cases) + { + var x1 = np.ones(new Shape(s1)); + var x2 = np.ones(new Shape(s2)); + + // Forward + var (a, b) = np.broadcast_arrays(x1, x2); + AssertShapeEqual(a, expected); + AssertShapeEqual(b, expected); + + // Reversed (symmetry) + var (a2, b2) = np.broadcast_arrays(x2, x1); + AssertShapeEqual(a2, expected); + AssertShapeEqual(b2, expected); + } + } + + /// + /// Incompatible shape pairs should throw. + /// From test_stride_tricks.py _test_broadcast_shapes_raise. + /// + /// >>> np.broadcast_arrays(np.ones(3), np.ones(4)) + /// ValueError: shape mismatch + /// + [TestMethod] + public void BroadcastArrays_IncompatibleShapesThrow() + { + var incompatible_pairs = new[] + { + (new int[] { 3 }, new int[] { 4 }), + (new int[] { 2, 3 }, new int[] { 2 }), + (new int[] { 1, 3, 4 }, new int[] { 2, 3, 3 }), + }; + + foreach (var (s1, s2) in incompatible_pairs) + { + var x1 = np.ones(new Shape(s1)); + var x2 = np.ones(new Shape(s2)); + + new Action(() => np.broadcast_arrays(x1, x2)) + .Should().Throw( + $"shapes ({string.Join(",", s1)}) and ({string.Join(",", s2)}) should be incompatible"); + + // Also reversed + new Action(() => np.broadcast_arrays(x2, x1)) + .Should().Throw( + $"shapes ({string.Join(",", s2)}) and ({string.Join(",", s1)}) should be incompatible (reversed)"); + } + } + + /// + /// Three-way incompatible: (3,) + (3,) + (4,) should throw. + /// >>> np.broadcast_arrays(np.ones(3), np.ones(3), np.ones(4)) + /// ValueError: shape mismatch + /// + [TestMethod] + public void BroadcastArrays_ThreeWayIncompatibleThrows() + { + var x1 = np.ones(new Shape(3)); + var x2 = np.ones(new Shape(3)); + var x3 = np.ones(new Shape(4)); + + new Action(() => np.broadcast_arrays(x1, x2, x3)) + .Should().Throw(); + } + + #endregion + + // ================================================================ + // Group 2: broadcast_to + // (from numpy/lib/tests/test_stride_tricks.py) + // ================================================================ + + #region Group 2: broadcast_to + + /// + /// broadcast_to succeeds for compatible shapes. + /// From test_stride_tricks.py test_broadcast_to_succeeds. + /// + /// Scalar cases: + /// np.broadcast_to(np.int32(0), (1,)) -> shape (1,) + /// np.broadcast_to(np.int32(0), (3,)) -> shape (3,) + /// + /// 1D cases: + /// np.broadcast_to(np.ones(1), (1,)) -> shape (1,) + /// np.broadcast_to(np.ones(1), (2,)) -> shape (2,) + /// np.broadcast_to(np.ones(1), (1,2,3)) -> shape (1,2,3) + /// + /// np.broadcast_to(np.arange(3), (3,)) -> shape (3,) + /// np.broadcast_to(np.arange(3), (1,3)) -> shape (1,3) + /// np.broadcast_to(np.arange(3), (2,3)) -> shape (2,3) + /// + [TestMethod] + public void BroadcastTo_Succeeds() + { + // Scalar to various shapes + var scalar = NDArray.Scalar(0); + + var r1 = np.broadcast_to(scalar, new Shape(1)); + AssertShapeEqual(r1, 1); + + var r2 = np.broadcast_to(scalar, new Shape(3)); + AssertShapeEqual(r2, 3); + + // ones(1) to various shapes + var o1 = np.ones(new Shape(1)); + + AssertShapeEqual(np.broadcast_to(o1, new Shape(1)), 1); + AssertShapeEqual(np.broadcast_to(o1, new Shape(2)), 2); + AssertShapeEqual(np.broadcast_to(o1, new Shape(1, 2, 3)), 1, 2, 3); + + // arange(3) to various shapes + var a3 = np.arange(3); + + AssertShapeEqual(np.broadcast_to(a3, new Shape(3)), 3); + AssertShapeEqual(np.broadcast_to(a3, new Shape(1, 3)), 1, 3); + AssertShapeEqual(np.broadcast_to(a3, new Shape(2, 3)), 2, 3); + } + + /// + /// broadcast_to succeeds for zero-size target shapes. + /// >>> np.broadcast_to(np.ones(1), (0,)).shape + /// (0,) + /// >>> np.broadcast_to(np.ones((1,2)), (0,2)).shape + /// (0, 2) + /// >>> np.broadcast_to(np.ones((2,1)), (2,0)).shape + /// (2, 0) + /// + [TestMethod] + public void BroadcastTo_ZeroSizeShapes() + { + AssertShapeEqual(np.broadcast_to(np.ones(new Shape(1)), new Shape(0)), 0); + AssertShapeEqual(np.broadcast_to(np.ones(new Shape(1, 2)), new Shape(0, 2)), 0, 2); + AssertShapeEqual(np.broadcast_to(np.ones(new Shape(2, 1)), new Shape(2, 0)), 2, 0); + } + + /// + /// broadcast_to raises for truly incompatible shapes where dimensions + /// conflict (neither is 1). + /// From test_stride_tricks.py test_broadcast_to_raises. + /// + /// >>> np.broadcast_to(np.ones(3), (2,)) + /// ValueError: ... + /// >>> np.broadcast_to(np.ones(3), (4,)) + /// ValueError: ... + /// + [TestMethod] + public void BroadcastTo_Raises() + { + // (3,) -> (2,) incompatible: dimension 3 vs 2, neither is 1 + new Action(() => np.broadcast_to(np.ones(new Shape(3)), new Shape(2))) + .Should().Throw(); + + // (3,) -> (4,) incompatible: dimension 3 vs 4, neither is 1 + new Action(() => np.broadcast_to(np.ones(new Shape(3)), new Shape(4))) + .Should().Throw(); + } + + /// + /// NumPy's broadcast_to is one-directional: source dims can only be 1 or match target. + /// NumSharp's broadcast_to uses bilateral Broadcast, so it allows cases NumPy rejects. + /// + /// Known discrepancies documented here: + /// + /// NumPy rejects (3,) -> (1,) but NumSharp resolves it as bilateral broadcast to (3,): + /// >>> np.broadcast_to(np.ones(3), (1,)) # NumPy: ValueError + /// + /// NumPy rejects (1,2) -> (2,1) but NumSharp resolves it to (2,2): + /// >>> np.broadcast_to(np.ones((1,2)), (2,1)) # NumPy: ValueError + /// + /// NumPy rejects (1,1) -> (1,) but NumSharp allows it: + /// >>> np.broadcast_to(np.ones((1,1)), (1,)) # NumPy: ValueError + /// + [TestMethod] + public void BroadcastTo_BilateralBroadcast_KnownDiscrepancy() + { + // NumSharp's broadcast_to uses bilateral broadcasting (DefaultEngine.Broadcast) + // rather than NumPy's one-directional broadcast_to semantics. + // These cases would throw in NumPy but succeed in NumSharp. + + // (3,) -> (1,): NumSharp broadcasts 1->3, result shape is (3,) + var r1 = np.broadcast_to(np.ones(new Shape(3)), new Shape(1)); + AssertShapeEqual(r1, 3); + + // (1,2) -> (2,1): NumSharp broadcasts both, result shape is (2,2) + var r2 = np.broadcast_to(np.ones(new Shape(1, 2)), new Shape(2, 1)); + AssertShapeEqual(r2, 2, 2); + + // (1,1) -> (1,): NumSharp resolves this via bilateral broadcast + var r3 = np.broadcast_to(np.ones(new Shape(1, 1)), new Shape(1)); + r3.Should().NotBeNull(); + } + + /// + /// broadcast_to returns a view (shared memory), not a copy. + /// From test_stride_tricks.py test_broadcast_to_is_view. + /// + /// >>> x = np.array([1, 2, 3]) + /// >>> y = np.broadcast_to(x, (2, 3)) + /// >>> y.base is x # (shares memory) + /// True + /// + [TestMethod] + public void BroadcastTo_IsView() + { + var x = np.array(new int[] { 1, 2, 3 }); + var y = np.broadcast_to(x, new Shape(2, 3)); + + AssertShapeEqual(y, 2, 3); + + // Verify it's a view by checking values match + // and that the broadcasted array shares data + Assert.AreEqual(1, y.GetInt32(0, 0)); + Assert.AreEqual(2, y.GetInt32(0, 1)); + Assert.AreEqual(3, y.GetInt32(0, 2)); + Assert.AreEqual(1, y.GetInt32(1, 0)); + Assert.AreEqual(2, y.GetInt32(1, 1)); + Assert.AreEqual(3, y.GetInt32(1, 2)); + } + + /// + /// Verify broadcast_to produces correct element values. + /// >>> x = np.arange(3) + /// >>> y = np.broadcast_to(x, (2, 3)) + /// >>> y + /// array([[0, 1, 2], + /// [0, 1, 2]]) + /// + [TestMethod] + public void BroadcastTo_ValuesCorrect() + { + var x = np.arange(3); // [0, 1, 2] + var y = np.broadcast_to(x, new Shape(2, 3)); + + var expected = np.array(new int[,] { { 0, 1, 2 }, { 0, 1, 2 } }); + np.array_equal(y, expected).Should().BeTrue(); + } + + /// + /// Verify broadcast_to with scalar input produces correct values. + /// >>> np.broadcast_to(np.int32(5), (3,)) + /// array([5, 5, 5]) + /// >>> np.broadcast_to(np.int32(5), (2, 3)) + /// array([[5, 5, 5], + /// [5, 5, 5]]) + /// + [TestMethod] + public void BroadcastTo_ScalarValues() + { + var scalar = NDArray.Scalar(5); + + var r1 = np.broadcast_to(scalar, new Shape(3)); + np.array_equal(r1, np.array(new int[] { 5, 5, 5 })).Should().BeTrue(); + + var r2 = np.broadcast_to(scalar, new Shape(2, 3)); + np.array_equal(r2, np.array(new int[,] { { 5, 5, 5 }, { 5, 5, 5 } })).Should().BeTrue(); + } + + /// + /// broadcast_to with (3,1) -> (3,3). + /// >>> x = np.array([[1],[2],[3]]) + /// >>> np.broadcast_to(x, (3,3)) + /// array([[1, 1, 1], + /// [2, 2, 2], + /// [3, 3, 3]]) + /// + [TestMethod] + public void BroadcastTo_ColumnToMatrix() + { + var x = np.array(new int[,] { { 1 }, { 2 }, { 3 } }); // shape (3,1) + var y = np.broadcast_to(x, new Shape(3, 3)); + + AssertShapeEqual(y, 3, 3); + var expected = np.array(new int[,] { { 1, 1, 1 }, { 2, 2, 2 }, { 3, 3, 3 } }); + np.array_equal(y, expected).Should().BeTrue(); + } + + #endregion + + // ================================================================ + // Group 3: broadcast_arrays Data Correctness + // (from numpy/lib/tests/test_stride_tricks.py) + // ================================================================ + + #region Group 3: broadcast_arrays Data Correctness + + /// + /// Verify that broadcast_arrays produces the same data layout + /// as ufunc broadcasting (via addition). + /// + /// For compatible shape pairs, broadcast_arrays(x0, x1) should + /// produce arrays whose element-wise sum equals x0 + x1. + /// + /// From test_stride_tricks.py test_same_as_ufunc. + /// + [TestMethod] + public void BroadcastArrays_SameAsUfunc() + { + var cases = new[] + { + (new int[] { 1 }, new int[] { 3 }), + (new int[] { 3 }, new int[] { 3 }), + (new int[] { 1, 3 }, new int[] { 3, 3 }), + (new int[] { 3, 1 }, new int[] { 3, 3 }), + (new int[] { 1, 3 }, new int[] { 3, 1 }), + (new int[] { 3 }, new int[] { 3, 3 }), + (new int[] { 3 }, new int[] { 1, 3 }), + (new int[] { 1 }, new int[] { 3, 3 }), + }; + + foreach (var (s1, s2) in cases) + { + var shape1 = new Shape(s1); + var shape2 = new Shape(s2); + var x0 = np.arange(shape1.size).reshape(shape1); + var x1 = np.arange(shape2.size).reshape(shape2); + + // Result from ufunc broadcasting + var ufunc_result = x0 + x1; + + // Result from broadcast_arrays + manual element-wise add + var (b0, b1) = np.broadcast_arrays(x0, x1); + var broadcast_result = b0 + b1; + + np.array_equal(ufunc_result, broadcast_result).Should().BeTrue( + $"broadcast_arrays + add should match ufunc add for shapes ({string.Join(",", s1)}) and ({string.Join(",", s2)})"); + } + } + + #endregion + + // ================================================================ + // Group 4: np.broadcast Object + // (from numpy/_core/tests/test_numeric.py) + // ================================================================ + + #region Group 4: np.broadcast Object + + /// + /// Test np.broadcast shape, ndim, size properties. + /// >>> arr1 = np.arange(6).reshape(2, 3) + /// >>> arr2 = np.arange(3) + /// >>> b = np.broadcast(arr1, arr2) + /// >>> b.shape + /// (2, 3) + /// >>> b.ndim + /// 2 + /// >>> b.size + /// 6 + /// + [TestMethod] + public void Broadcast_Properties() + { + var arr1 = np.arange(6).reshape(2, 3); + var arr2 = np.arange(3); + var b = np.broadcast(arr1, arr2); + + AssertShapeEqual(b.shape, 2, 3); + Assert.AreEqual(2, b.ndim); + Assert.AreEqual(6, b.size); + Assert.AreEqual(b.ndim, b.nd); // nd is alias for ndim + } + + /// + /// Test np.broadcast with shapes that require broadcasting. + /// >>> arr1 = np.ones((2, 1)) + /// >>> arr2 = np.ones((1, 3)) + /// >>> b = np.broadcast(arr1, arr2) + /// >>> b.shape + /// (2, 3) + /// >>> b.size + /// 6 + /// + [TestMethod] + public void Broadcast_BroadcastedShape() + { + var arr1 = np.ones(new Shape(2, 1)); + var arr2 = np.ones(new Shape(1, 3)); + var b = np.broadcast(arr1, arr2); + + AssertShapeEqual(b.shape, 2, 3); + Assert.AreEqual(6, b.size); + Assert.AreEqual(2, b.ndim); + } + + /// + /// Test np.broadcast with single-element arrays. + /// >>> arr = np.arange(6).reshape(2, 3) + /// >>> b = np.broadcast(arr, np.int32(1)) + /// >>> b.shape + /// (2, 3) + /// + [TestMethod] + public void Broadcast_WithScalar() + { + var arr = np.arange(6).reshape(2, 3); + var scalar = NDArray.Scalar(1); + var b = np.broadcast(arr, scalar); + + AssertShapeEqual(b.shape, 2, 3); + Assert.AreEqual(6, b.size); + } + + /// + /// Test np.broadcast with 4D broadcasting. + /// >>> b = np.broadcast(np.ones((8,1,6,1)), np.ones((7,1,5))) + /// >>> b.shape + /// (8, 7, 6, 5) + /// >>> b.size + /// 1680 + /// + [TestMethod] + public void Broadcast_4D() + { + var arr1 = np.ones(new Shape(8, 1, 6, 1)); + var arr2 = np.ones(new Shape(7, 1, 5)); + var b = np.broadcast(arr1, arr2); + + AssertShapeEqual(b.shape, 8, 7, 6, 5); + Assert.AreEqual(8 * 7 * 6 * 5, b.size); + Assert.AreEqual(4, b.ndim); + } + + /// + /// Test np.broadcast iterators array. + /// >>> b = np.broadcast(np.arange(3), np.ones((2, 3))) + /// >>> len(b.iters) + /// 2 + /// + [TestMethod] + public void Broadcast_HasIters() + { + var arr1 = np.arange(3); + var arr2 = np.ones(new Shape(2, 3)); + var b = np.broadcast(arr1, arr2); + + b.iters.Should().NotBeNull(); + b.iters.Length.Should().Be(2); + } + + #endregion + + // ================================================================ + // Group 5: Arithmetic Broadcasting End-to-End + // ================================================================ + + #region Group 5: Arithmetic Broadcasting End-to-End + + /// + /// Scalar + Array and Array + Scalar. + /// >>> np.int32(1) + np.array([1, 2, 3]) + /// array([2, 3, 4]) + /// >>> np.array([1, 2, 3]) + np.int32(10) + /// array([11, 12, 13]) + /// + [TestMethod] + public void Add_ScalarAndArray() + { + var arr = np.array(new int[] { 1, 2, 3 }); + var scalar = NDArray.Scalar(1); + + // scalar + array + var r1 = scalar + arr; + np.array_equal(r1, np.array(new int[] { 2, 3, 4 })).Should().BeTrue(); + + // array + scalar + var scalar10 = NDArray.Scalar(10); + var r2 = arr + scalar10; + np.array_equal(r2, np.array(new int[] { 11, 12, 13 })).Should().BeTrue(); + } + + /// + /// 1D broadcast against 2D. + /// >>> np.arange(6).reshape(2, 3) + np.arange(3) + /// array([[0, 2, 4], + /// [3, 5, 7]]) + /// + [TestMethod] + public void Add_1DTo2D() + { + var a = np.arange(6).reshape(2, 3); // [[0,1,2],[3,4,5]] + var b = np.arange(3); // [0,1,2] + var result = a + b; + + AssertShapeEqual(result, 2, 3); + var expected = np.array(new int[,] { { 0, 2, 4 }, { 3, 5, 7 } }); + np.array_equal(result, expected).Should().BeTrue(); + } + + /// + /// (3,1) + (1,3) broadcasting. + /// >>> np.array([[0],[1],[2]]) + np.array([[0, 1, 2]]) + /// array([[0, 1, 2], + /// [1, 2, 3], + /// [2, 3, 4]]) + /// + [TestMethod] + public void Add_ColumnPlusRow() + { + var col = np.array(new int[,] { { 0 }, { 1 }, { 2 } }); // shape (3,1) + var row = np.array(new int[,] { { 0, 1, 2 } }); // shape (1,3) + var result = col + row; + + AssertShapeEqual(result, 3, 3); + var expected = np.array(new int[,] { { 0, 1, 2 }, { 1, 2, 3 }, { 2, 3, 4 } }); + np.array_equal(result, expected).Should().BeTrue(); + } + + /// + /// 4D broadcasting: (8,1,6,1) + (7,1,5) = (8,7,6,5). + /// Verify shape only (values would be huge). + /// >>> (np.ones((8,1,6,1)) + np.ones((7,1,5))).shape + /// (8, 7, 6, 5) + /// + [TestMethod] + public void Add_4DBroadcast() + { + var a = np.ones(new Shape(8, 1, 6, 1)); + var b = np.ones(new Shape(7, 1, 5)); + var result = a + b; + + AssertShapeEqual(result, 8, 7, 6, 5); + + // All values should be 2.0 (1+1) + Assert.AreEqual(2.0, result.GetDouble(0, 0, 0, 0)); + Assert.AreEqual(2.0, result.GetDouble(7, 6, 5, 4)); + } + + /// + /// Subtraction with broadcasting. + /// >>> np.arange(6).reshape(2, 3) - np.arange(3) + /// array([[0, 0, 0], + /// [3, 3, 3]]) + /// + [TestMethod] + public void Subtract_Broadcast() + { + var a = np.arange(6).reshape(2, 3); // [[0,1,2],[3,4,5]] + var b = np.arange(3); // [0,1,2] + var result = a - b; + + AssertShapeEqual(result, 2, 3); + var expected = np.array(new int[,] { { 0, 0, 0 }, { 3, 3, 3 } }); + np.array_equal(result, expected).Should().BeTrue(); + } + + /// + /// Multiplication with broadcasting. + /// >>> np.arange(6).reshape(2, 3) * np.array([1, 2, 3]) + /// array([[ 0, 2, 6], + /// [ 3, 8, 15]]) + /// + [TestMethod] + public void Multiply_Broadcast() + { + var a = np.arange(6).reshape(2, 3); // [[0,1,2],[3,4,5]] + var b = np.array(new int[] { 1, 2, 3 }); + var result = a * b; + + AssertShapeEqual(result, 2, 3); + var expected = np.array(new int[,] { { 0, 2, 6 }, { 3, 8, 15 } }); + np.array_equal(result, expected).Should().BeTrue(); + } + + /// + /// Element-wise comparison with scalar broadcasting. + /// NumSharp's > operator only supports NDArray > scalar (not NDArray > NDArray + /// with broadcasting). This test verifies scalar comparison works correctly. + /// + /// >>> np.arange(6).reshape(2, 3) > 1 + /// array([[False, False, True], + /// [ True, True, True]]) + /// + [TestMethod] + public void Comparison_Broadcast() + { + var a = np.arange(6).reshape(2, 3); // [[0,1,2],[3,4,5]] + var result = a > 1; + + AssertShapeEqual(result, 2, 3); + Assert.AreEqual(false, result.GetBoolean(0, 0)); // 0 > 1 = false + Assert.AreEqual(false, result.GetBoolean(0, 1)); // 1 > 1 = false + Assert.AreEqual(true, result.GetBoolean(0, 2)); // 2 > 1 = true + Assert.AreEqual(true, result.GetBoolean(1, 0)); // 3 > 1 = true + Assert.AreEqual(true, result.GetBoolean(1, 1)); // 4 > 1 = true + Assert.AreEqual(true, result.GetBoolean(1, 2)); // 5 > 1 = true + } + + #endregion + + // ================================================================ + // Group 6: Edge Cases + // ================================================================ + + #region Group 6: Edge Cases + + /// + /// Scalar NDArray broadcast to various shapes. + /// >>> np.broadcast_to(np.float64(1.0), (3, 4)) + /// array([[1., 1., 1., 1.], + /// [1., 1., 1., 1.], + /// [1., 1., 1., 1.]]) + /// + [TestMethod] + public void BroadcastTo_ScalarToShape() + { + var scalar = NDArray.Scalar(1.0); + + var r1 = np.broadcast_to(scalar, new Shape(3)); + AssertShapeEqual(r1, 3); + Assert.AreEqual(1.0, r1.GetDouble(0)); + Assert.AreEqual(1.0, r1.GetDouble(2)); + + var r2 = np.broadcast_to(scalar, new Shape(3, 4)); + AssertShapeEqual(r2, 3, 4); + Assert.AreEqual(1.0, r2.GetDouble(0, 0)); + Assert.AreEqual(1.0, r2.GetDouble(2, 3)); + + var r3 = np.broadcast_to(scalar, new Shape(2, 3, 4)); + AssertShapeEqual(r3, 2, 3, 4); + Assert.AreEqual(1.0, r3.GetDouble(0, 0, 0)); + Assert.AreEqual(1.0, r3.GetDouble(1, 2, 3)); + } + + /// + /// broadcast_arrays returns views (shared memory). + /// From test_stride_tricks.py. + /// + /// >>> x = np.array([1, 2, 3]) + /// >>> y = np.array([[1], [2]]) + /// >>> a, b = np.broadcast_arrays(x, y) + /// >>> a.shape + /// (2, 3) + /// + /// Verify that a/b reflect the original data. + /// + [TestMethod] + public void BroadcastArrays_ReturnsViews() + { + var x = np.array(new int[] { 1, 2, 3 }); // shape (3,) + var y = np.array(new int[,] { { 1 }, { 2 } }); // shape (2,1) + var (a, b) = np.broadcast_arrays(x, y); + + AssertShapeEqual(a, 2, 3); + AssertShapeEqual(b, 2, 3); + + // a should broadcast x across rows: [[1,2,3],[1,2,3]] + Assert.AreEqual(1, a.GetInt32(0, 0)); + Assert.AreEqual(2, a.GetInt32(0, 1)); + Assert.AreEqual(3, a.GetInt32(0, 2)); + Assert.AreEqual(1, a.GetInt32(1, 0)); + Assert.AreEqual(2, a.GetInt32(1, 1)); + Assert.AreEqual(3, a.GetInt32(1, 2)); + + // b should broadcast y across columns: [[1,1,1],[2,2,2]] + Assert.AreEqual(1, b.GetInt32(0, 0)); + Assert.AreEqual(1, b.GetInt32(0, 1)); + Assert.AreEqual(1, b.GetInt32(0, 2)); + Assert.AreEqual(2, b.GetInt32(1, 0)); + Assert.AreEqual(2, b.GetInt32(1, 1)); + Assert.AreEqual(2, b.GetInt32(1, 2)); + } + + /// + /// Broadcasting works correctly with sliced (view) arrays. + /// >>> x = np.arange(12).reshape(3, 4) + /// >>> y = x[:, 0:1] # shape (3, 1) - a view + /// >>> z = np.broadcast_to(y, (3, 4)) + /// >>> z + /// array([[0, 0, 0, 0], + /// [4, 4, 4, 4], + /// [8, 8, 8, 8]]) + /// + [TestMethod] + public void Broadcast_SlicedInput() + { + var x = np.arange(12).reshape(3, 4); + // x = [[0,1,2,3],[4,5,6,7],[8,9,10,11]] + var y = x[":, 0:1"]; // shape (3,1), values [0],[4],[8] + + AssertShapeEqual(y, 3, 1); + Assert.AreEqual(0, y.GetInt32(0, 0)); + Assert.AreEqual(4, y.GetInt32(1, 0)); + Assert.AreEqual(8, y.GetInt32(2, 0)); + + var z = np.broadcast_to(y, new Shape(3, 4)); + AssertShapeEqual(z, 3, 4); + + var expected = np.array(new int[,] { { 0, 0, 0, 0 }, { 4, 4, 4, 4 }, { 8, 8, 8, 8 } }); + np.array_equal(z, expected).Should().BeTrue(); + } + + /// + /// Broadcasting with sliced input in arithmetic. + /// >>> x = np.arange(12).reshape(3, 4) + /// >>> y = x[:, 0:1] # column 0 as (3,1) + /// >>> result = x + y + /// >>> result + /// array([[ 0, 1, 2, 3], + /// [ 8, 9, 10, 11], + /// [16, 17, 18, 19]]) + /// + [TestMethod] + public void Broadcast_SlicedInputArithmetic() + { + var x = np.arange(12).reshape(3, 4); + var y = x[":, 0:1"]; // shape (3,1): [[0],[4],[8]] + var result = x + y; + + AssertShapeEqual(result, 3, 4); + var expected = np.array(new int[,] + { + { 0, 1, 2, 3 }, + { 8, 9, 10, 11 }, + { 16, 17, 18, 19 } + }); + np.array_equal(result, expected).Should().BeTrue(); + } + + /// + /// 5D broadcasting. + /// >>> a = np.ones((2, 1, 3, 1, 5)) + /// >>> b = np.ones((1, 4, 1, 6, 1)) + /// >>> (a + b).shape + /// (2, 4, 3, 6, 5) + /// + [TestMethod] + public void Broadcast_HighDimensional() + { + var a = np.ones(new Shape(2, 1, 3, 1, 5)); + var b = np.ones(new Shape(1, 4, 1, 6, 1)); + var result = a + b; + + AssertShapeEqual(result, 2, 4, 3, 6, 5); + + // All values should be 2.0 + Assert.AreEqual(2.0, result.GetDouble(0, 0, 0, 0, 0)); + Assert.AreEqual(2.0, result.GetDouble(1, 3, 2, 5, 4)); + } + + /// + /// 6D broadcasting. + /// >>> a = np.ones((1, 2, 1, 3, 1, 4)) + /// >>> b = np.ones((5, 1, 6, 1, 7, 1)) + /// >>> (a + b).shape + /// (5, 2, 6, 3, 7, 4) + /// + [TestMethod] + public void Broadcast_6D() + { + var a = np.ones(new Shape(1, 2, 1, 3, 1, 4)); + var b = np.ones(new Shape(5, 1, 6, 1, 7, 1)); + var result = a + b; + + AssertShapeEqual(result, 5, 2, 6, 3, 7, 4); + Assert.AreEqual(2.0, result.GetDouble(0, 0, 0, 0, 0, 0)); + } + + /// + /// broadcast_arrays with three inputs. + /// >>> a = np.ones((2, 1)) + /// >>> b = np.ones((1, 3)) + /// >>> c = np.ones((2, 3)) + /// >>> r = np.broadcast_arrays(a, b, c) + /// >>> [x.shape for x in r] + /// [(2, 3), (2, 3), (2, 3)] + /// + [TestMethod] + public void BroadcastArrays_ThreeInputs() + { + var a = np.ones(new Shape(2, 1)); + var b = np.ones(new Shape(1, 3)); + var c = np.ones(new Shape(2, 3)); + var result = np.broadcast_arrays(a, b, c); + + result.Length.Should().Be(3); + foreach (var r in result) + AssertShapeEqual(r, 2, 3); + } + + /// + /// broadcast_arrays with four inputs of varying dimensions. + /// >>> a = np.ones((6, 7)) + /// >>> b = np.ones((5, 6, 1)) + /// >>> c = np.ones((7,)) + /// >>> d = np.ones((5, 1, 7)) + /// >>> r = np.broadcast_arrays(a, b, c, d) + /// >>> [x.shape for x in r] + /// [(5, 6, 7), (5, 6, 7), (5, 6, 7), (5, 6, 7)] + /// + [TestMethod] + public void BroadcastArrays_FourInputs() + { + var a = np.ones(new Shape(6, 7)); + var b = np.ones(new Shape(5, 6, 1)); + var c = np.ones(new Shape(7)); + var d = np.ones(new Shape(5, 1, 7)); + var result = np.broadcast_arrays(a, b, c, d); + + result.Length.Should().Be(4); + foreach (var r in result) + AssertShapeEqual(r, 5, 6, 7); + } + + /// + /// ResolveReturnShape: classic broadcasting examples from NumPy docs. + /// Validates the low-level shape resolution. + /// + [TestMethod] + public void ResolveReturnShape_ClassicExamples() + { + // (256,256,3) + (3,) -> (256,256,3) + var s1 = DefaultEngine.ResolveReturnShape(new Shape(256, 256, 3), new Shape(3)); + s1.dimensions.Should().BeEquivalentTo(new[] { 256, 256, 3 }); + + // (5,4) + (1,) -> (5,4) + var s2 = DefaultEngine.ResolveReturnShape(new Shape(5, 4), new Shape(1)); + s2.dimensions.Should().BeEquivalentTo(new[] { 5, 4 }); + + // (5,4) + (4,) -> (5,4) + var s3 = DefaultEngine.ResolveReturnShape(new Shape(5, 4), new Shape(4)); + s3.dimensions.Should().BeEquivalentTo(new[] { 5, 4 }); + + // (15,3,5) + (15,1,5) -> (15,3,5) + var s4 = DefaultEngine.ResolveReturnShape(new Shape(15, 3, 5), new Shape(15, 1, 5)); + s4.dimensions.Should().BeEquivalentTo(new[] { 15, 3, 5 }); + + // (15,3,5) + (3,1) -> (15,3,5) + var s5 = DefaultEngine.ResolveReturnShape(new Shape(15, 3, 5), new Shape(3, 1)); + s5.dimensions.Should().BeEquivalentTo(new[] { 15, 3, 5 }); + } + + /// + /// are_broadcastable returns true for compatible and false for incompatible shapes. + /// + [TestMethod] + public void AreBroadcastable_CompatibleAndIncompatible() + { + // Compatible pairs + np.are_broadcastable(new int[] { 1, 3 }, new int[] { 3, 1 }).Should().BeTrue(); + np.are_broadcastable(new int[] { 5, 4 }, new int[] { 1 }).Should().BeTrue(); + np.are_broadcastable(new int[] { 5, 4 }, new int[] { 4 }).Should().BeTrue(); + np.are_broadcastable(new int[] { 8, 1, 6, 1 }, new int[] { 7, 1, 5 }).Should().BeTrue(); + + // Incompatible pairs + np.are_broadcastable(new int[] { 3 }, new int[] { 4 }).Should().BeFalse(); + np.are_broadcastable(new int[] { 2, 1 }, new int[] { 8, 4, 3 }).Should().BeFalse(); + np.are_broadcastable(new int[] { 1, 3, 4 }, new int[] { 2, 3, 3 }).Should().BeFalse(); + } + + /// + /// Broadcasting with float types. + /// >>> np.float64(2.5) + np.array([1.0, 2.0, 3.0]) + /// array([3.5, 4.5, 5.5]) + /// + [TestMethod] + public void Add_BroadcastFloat() + { + var scalar = NDArray.Scalar(2.5); + var arr = np.array(new double[] { 1.0, 2.0, 3.0 }); + var result = scalar + arr; + + AssertShapeEqual(result, 3); + Assert.AreEqual(3.5, result.GetDouble(0)); + Assert.AreEqual(4.5, result.GetDouble(1)); + Assert.AreEqual(5.5, result.GetDouble(2)); + } + + /// + /// Verify broadcast_to with (1,) -> (1,) is identity. + /// >>> x = np.array([42]) + /// >>> y = np.broadcast_to(x, (1,)) + /// >>> y[0] + /// 42 + /// + [TestMethod] + public void BroadcastTo_IdentityShape() + { + var x = np.array(new int[] { 42 }); + var y = np.broadcast_to(x, new Shape(1)); + + AssertShapeEqual(y, 1); + Assert.AreEqual(42, y.GetInt32(0)); + } + + /// + /// Verify broadcast with (1,1) + (3,3) -> (3,3). + /// >>> np.ones((1,1)) + np.arange(9).reshape(3,3) + /// array([[ 1., 2., 3.], + /// [ 4., 5., 6.], + /// [ 7., 8., 9.]]) + /// + [TestMethod] + public void Add_1x1_Plus_3x3() + { + var a = np.ones(new Shape(1, 1)); // [[1.0]] + var b = np.arange(9).reshape(3, 3); // [[0,1,2],[3,4,5],[6,7,8]] + var result = a + b; + + AssertShapeEqual(result, 3, 3); + Assert.AreEqual(1.0, result.GetDouble(0, 0)); + Assert.AreEqual(2.0, result.GetDouble(0, 1)); + Assert.AreEqual(9.0, result.GetDouble(2, 2)); + } + + /// + /// Verify broadcasting produces correct stride pattern. + /// When a dimension is broadcast, its stride should be 0. + /// + /// >>> x = np.array([1, 2, 3]) + /// >>> y = np.broadcast_to(x, (4, 3)) + /// >>> y.strides + /// (0, 4) # (0 bytes for first dim since it's broadcasted, 4 bytes for int32) + /// + [TestMethod] + public void BroadcastTo_StridesCorrect() + { + var x = np.array(new int[] { 1, 2, 3 }); // shape (3,) + var y = np.broadcast_to(x, new Shape(4, 3)); + + AssertShapeEqual(y, 4, 3); + + // The broadcast dimension (dim 0, size 4) should have stride 0 + y.Shape.strides[0].Should().Be(0, + "broadcast dimension should have stride 0"); + + // The non-broadcast dimension (dim 1, size 3) should have stride 1 + y.Shape.strides[1].Should().Be(1, + "non-broadcast dimension should have stride 1 (element stride)"); + } + + /// + /// Verify DefaultEngine.Broadcast produces correct strides for + /// a dimension-1 that gets stretched. + /// + /// >>> x = np.ones((3, 1)) + /// >>> y = np.ones((1, 4)) + /// >>> bx, by = np.broadcast_arrays(x, y) + /// >>> bx.strides + /// (8, 0) # 8 bytes stride for rows, 0 for broadcast cols + /// >>> by.strides + /// (0, 8) # 0 for broadcast rows, 8 bytes stride for cols + /// + [TestMethod] + public void BroadcastArrays_StridesCorrect() + { + var x = np.ones(new Shape(3, 1)); + var y = np.ones(new Shape(1, 4)); + var (bx, by) = np.broadcast_arrays(x, y); + + AssertShapeEqual(bx, 3, 4); + AssertShapeEqual(by, 3, 4); + + // bx: row stride should be non-zero, col stride should be 0 (broadcast) + bx.Shape.strides[0].Should().NotBe(0, "row stride should be original"); + bx.Shape.strides[1].Should().Be(0, "broadcast col should have stride 0"); + + // by: row stride should be 0 (broadcast), col stride should be non-zero + by.Shape.strides[0].Should().Be(0, "broadcast row should have stride 0"); + by.Shape.strides[1].Should().NotBe(0, "col stride should be original"); + } + + /// + /// broadcast_to with reversed-stride (negative step) input. + /// NOTE: GetInt32/NDIterator/array_equal return correct values, + /// but ToString has a known bug where it ignores negative strides + /// on broadcasted arrays, displaying elements in wrong order. + /// + /// >>> x = np.arange(3)[::-1] # [2, 1, 0] + /// >>> np.broadcast_to(x, (2, 3)) + /// array([[2, 1, 0], + /// [2, 1, 0]]) + /// + [TestMethod] + public void BroadcastTo_ReversedSlice() + { + var rev = np.arange(3)["::-1"]; // [2, 1, 0] + Assert.AreEqual(2, rev.GetInt32(0)); + Assert.AreEqual(1, rev.GetInt32(1)); + Assert.AreEqual(0, rev.GetInt32(2)); + + var brev = np.broadcast_to(rev, new Shape(2, 3)); + AssertShapeEqual(brev, 2, 3); + + // Verify correct values via element access + Assert.AreEqual(2, brev.GetInt32(0, 0)); + Assert.AreEqual(1, brev.GetInt32(0, 1)); + Assert.AreEqual(0, brev.GetInt32(0, 2)); + Assert.AreEqual(2, brev.GetInt32(1, 0)); + Assert.AreEqual(1, brev.GetInt32(1, 1)); + Assert.AreEqual(0, brev.GetInt32(1, 2)); + + // Verify via array_equal + var expected = np.array(new int[,] { { 2, 1, 0 }, { 2, 1, 0 } }); + np.array_equal(brev, expected).Should().BeTrue(); + } + + /// + /// broadcast_to with step-sliced (step=2) input. + /// NOTE: Same ToString bug as reversed slice — values via element + /// access and array_equal are correct. + /// + /// >>> x = np.arange(6)[::2] # [0, 2, 4] + /// >>> np.broadcast_to(x, (2, 3)) + /// array([[0, 2, 4], + /// [0, 2, 4]]) + /// + [TestMethod] + public void BroadcastTo_StepSlice() + { + var stepped = np.arange(6)["::2"]; // [0, 2, 4] + Assert.AreEqual(0, stepped.GetInt32(0)); + Assert.AreEqual(2, stepped.GetInt32(1)); + Assert.AreEqual(4, stepped.GetInt32(2)); + + var bstepped = np.broadcast_to(stepped, new Shape(2, 3)); + AssertShapeEqual(bstepped, 2, 3); + + Assert.AreEqual(0, bstepped.GetInt32(0, 0)); + Assert.AreEqual(2, bstepped.GetInt32(0, 1)); + Assert.AreEqual(4, bstepped.GetInt32(0, 2)); + Assert.AreEqual(0, bstepped.GetInt32(1, 0)); + Assert.AreEqual(2, bstepped.GetInt32(1, 1)); + Assert.AreEqual(4, bstepped.GetInt32(1, 2)); + + var expected = np.array(new int[,] { { 0, 2, 4 }, { 0, 2, 4 } }); + np.array_equal(bstepped, expected).Should().BeTrue(); + } + + /// + /// broadcast_to with 2D sliced column then broadcast. + /// NOTE: ToString bug — shows zeros in last row instead of correct values. + /// Element access and array_equal are correct. + /// + /// >>> x = np.arange(12).reshape(3, 4) + /// >>> col = x[:, 1:2] # [[1],[5],[9]] + /// >>> np.broadcast_to(col, (3, 3)) + /// array([[1, 1, 1], + /// [5, 5, 5], + /// [9, 9, 9]]) + /// + [TestMethod] + public void BroadcastTo_SlicedColumn() + { + var x = np.arange(12).reshape(3, 4); + var col = x[":, 1:2"]; // shape (3,1): [[1],[5],[9]] + + AssertShapeEqual(col, 3, 1); + Assert.AreEqual(1, col.GetInt32(0, 0)); + Assert.AreEqual(5, col.GetInt32(1, 0)); + Assert.AreEqual(9, col.GetInt32(2, 0)); + + var bcol = np.broadcast_to(col, new Shape(3, 3)); + AssertShapeEqual(bcol, 3, 3); + + var expected = np.array(new int[,] { { 1, 1, 1 }, { 5, 5, 5 }, { 9, 9, 9 } }); + np.array_equal(bcol, expected).Should().BeTrue(); + } + + /// + /// Double-sliced (row step + column slice) then broadcast. + /// NOTE: ToString shows wrong values for second row. + /// Element access and array_equal are correct. + /// + /// >>> x = np.arange(12).reshape(3, 4) + /// >>> ds = x[::2, 0:1] # [[0],[8]] + /// >>> np.broadcast_to(ds, (2, 4)) + /// array([[0, 0, 0, 0], + /// [8, 8, 8, 8]]) + /// + [TestMethod] + public void BroadcastTo_DoubleSliced() + { + var x = np.arange(12).reshape(3, 4); + var dslice = x["::2, :"]; // rows 0 and 2: shape (2,4) + var dslice_col = dslice[":, 0:1"]; // shape (2,1): [[0],[8]] + + Assert.AreEqual(0, dslice_col.GetInt32(0, 0)); + Assert.AreEqual(8, dslice_col.GetInt32(1, 0)); + + var bdslice = np.broadcast_to(dslice_col, new Shape(2, 4)); + AssertShapeEqual(bdslice, 2, 4); + + var expected = np.array(new int[,] { { 0, 0, 0, 0 }, { 8, 8, 8, 8 } }); + np.array_equal(bdslice, expected).Should().BeTrue(); + } + + /// + /// Arithmetic with reversed-slice + broadcast produces correct results. + /// >>> x = np.arange(3)[::-1] + /// >>> y = np.broadcast_to(x, (2, 3)) + /// >>> y + np.ones((2, 3), dtype=np.int32) + /// array([[3, 2, 1], + /// [3, 2, 1]]) + /// + [TestMethod] + public void Add_ReversedSliceBroadcast() + { + var rev = np.arange(3)["::-1"]; + var brev = np.broadcast_to(rev, new Shape(2, 3)); + var ones = np.ones(new Shape(2, 3), NPTypeCode.Int32); + var result = brev + ones; + + AssertShapeEqual(result, 2, 3); + var expected = np.array(new int[,] { { 3, 2, 1 }, { 3, 2, 1 } }); + np.array_equal(result, expected).Should().BeTrue(); + } + + /// + /// np.sum on broadcast_to of a sliced column. + /// >>> x = np.arange(12).reshape(3, 4) + /// >>> col = x[:, 2:3] # [[2],[6],[10]] + /// >>> bc = np.broadcast_to(col, (3, 5)) + /// >>> np.sum(bc) + /// 90 + /// >>> np.sum(bc, axis=0) + /// array([18, 18, 18, 18, 18]) + /// >>> np.sum(bc, axis=1) + /// array([10, 30, 50]) + /// + [TestMethod] + public void Sum_SlicedBroadcast() + { + var x = np.arange(12).reshape(3, 4); + var col = x[":, 2:3"]; // shape (3,1): [[2],[6],[10]] + var bc = np.broadcast_to(col, new Shape(3, 5)); + + // Total sum: 2*5 + 6*5 + 10*5 = 90 + Assert.AreEqual(90, (int)np.sum(bc)); + + // Sum along axis 0: [2+6+10, ...] = [18,18,18,18,18] + var s0 = np.sum(bc, axis: 0); + AssertShapeEqual(s0, 5); + for (int c = 0; c < 5; c++) + Assert.AreEqual(18, s0.GetInt32(c)); + + // Sum along axis 1: [2*5, 6*5, 10*5] = [10,30,50] + var s1 = np.sum(bc, axis: 1); + AssertShapeEqual(s1, 3); + Assert.AreEqual(10, s1.GetInt32(0)); + Assert.AreEqual(30, s1.GetInt32(1)); + Assert.AreEqual(50, s1.GetInt32(2)); + } + + /// + /// Flatten and copy of sliced+broadcast arrays produce correct data. + /// >>> x = np.arange(3)[::-1] + /// >>> bc = np.broadcast_to(x, (2, 3)) + /// >>> bc.flatten() + /// array([2, 1, 0, 2, 1, 0]) + /// >>> np.copy(bc) + /// array([[2, 1, 0], + /// [2, 1, 0]]) + /// + [TestMethod] + public void FlattenAndCopy_SlicedBroadcast() + { + var rev = np.arange(3)["::-1"]; + var brev = np.broadcast_to(rev, new Shape(2, 3)); + + // flatten should produce [2,1,0,2,1,0] + var flat = brev.flatten(); + AssertShapeEqual(flat, 6); + var expected_flat = np.array(new int[] { 2, 1, 0, 2, 1, 0 }); + np.array_equal(flat, expected_flat).Should().BeTrue(); + + // copy should produce a contiguous copy with correct values + var copied = np.copy(brev); + AssertShapeEqual(copied, 2, 3); + var expected_copy = np.array(new int[,] { { 2, 1, 0 }, { 2, 1, 0 } }); + np.array_equal(copied, expected_copy).Should().BeTrue(); + } + + #endregion + + // ================================================================ + // Group 7: Stress Tests & Bug Probes + // Systematically exercising every broadcast code path. + // ================================================================ + + #region Group 7: Stress Tests & Bug Probes + + /// + /// broadcast_to result allows write-through to original (no read-only protection). + /// NumPy raises ValueError: assignment destination is read-only. + /// + /// Known NumSharp bug: broadcast_to does not enforce read-only semantics. + /// Writing to the broadcast view modifies the original array. + /// + /// >>> x = np.array([1, 2, 3, 4]) + /// >>> y = np.broadcast_to(x, (2, 4)) + /// >>> y[0, 0] = 999 # NumPy: ValueError + /// + [TestMethod] + public void BroadcastTo_WriteThrough_KnownBug() + { + var x = np.array(new int[] { 1, 2, 3, 4 }); + var bx = np.broadcast_to(x, new Shape(2, 4)); + + // NumSharp allows writing through broadcast view to original. + // This is a known bug — NumPy would throw. + // We document the current behavior: SetInt32 modifies original. + bx.SetInt32(999, 0, 0); + Assert.AreEqual(999, x.GetInt32(0), + "NumSharp bug: broadcast_to write-through modifies original. " + + "NumPy would throw ValueError: assignment destination is read-only."); + + // Restore for safety + x.SetInt32(1, 0); + } + + /// + /// Re-broadcasting an already-broadcasted array via broadcast_arrays + /// succeeds in NumSharp (the N-array Broadcast overload doesn't guard against it). + /// The 2-array Broadcast(Shape,Shape) overload throws NotSupportedException. + /// + /// This is inconsistent behavior. + /// + [TestMethod] + public void BroadcastArrays_AlreadyBroadcasted_Succeeds() + { + var x = np.ones(new Shape(1, 3)); + var y = np.ones(new Shape(3, 1)); + var (bx, by) = np.broadcast_arrays(x, y); + + // bx is now a broadcasted (3,3). broadcast_arrays with another array succeeds. + var (rbx, rby) = np.broadcast_arrays(bx, np.ones(new Shape(3, 3))); + AssertShapeEqual(rbx, 3, 3); + AssertShapeEqual(rby, 3, 3); + } + + /// + /// broadcast_to on an already-broadcasted array throws NotSupportedException. + /// + [TestMethod] + public void BroadcastTo_AlreadyBroadcasted_Throws() + { + var x = np.ones(new Shape(1, 3)); + var bx = np.broadcast_to(x, new Shape(4, 3)); + + new Action(() => np.broadcast_to(bx, new Shape(2, 4, 3))) + .Should().Throw(); + } + + /// + /// Dimension-0 (zero-size) array broadcasting. + /// >>> np.broadcast_arrays(np.ones((0, 3)), np.ones((1, 3))) + /// Both shapes become (0, 3). + /// + [TestMethod] + public void BroadcastArrays_ZeroSizeDimension() + { + var z = np.ones(new Shape(0, 3)); + var o = np.ones(new Shape(1, 3)); + var (bz, bo) = np.broadcast_arrays(z, o); + + AssertShapeEqual(bz, 0, 3); + AssertShapeEqual(bo, 0, 3); + } + + /// + /// (0,3) + (2,3) should be incompatible (0 != 2, neither is 1). + /// >>> np.broadcast_arrays(np.ones((0, 3)), np.ones((2, 3))) + /// ValueError: shape mismatch + /// + [TestMethod] + public void BroadcastArrays_ZeroVsNonZeroDim_Throws() + { + var z = np.ones(new Shape(0, 3)); + var o = np.ones(new Shape(2, 3)); + + new Action(() => np.broadcast_arrays(z, o)) + .Should().Throw(); + } + + /// + /// All 12 dtypes can broadcast via arithmetic (shape correctness). + /// (1,3) + (3,1) -> (3,3) for every supported type. + /// + [TestMethod] + public void Add_BroadcastAllDtypes() + { + var dtypes = new NPTypeCode[] + { + NPTypeCode.Boolean, NPTypeCode.Byte, NPTypeCode.Int16, NPTypeCode.UInt16, + NPTypeCode.Int32, NPTypeCode.UInt32, NPTypeCode.Int64, NPTypeCode.UInt64, + NPTypeCode.Char, NPTypeCode.Single, NPTypeCode.Double, NPTypeCode.Decimal + }; + + foreach (var dt in dtypes) + { + var a = np.ones(new Shape(1, 3), dt); + var b = np.ones(new Shape(3, 1), dt); + var result = a + b; + AssertShapeEqual(result, 3, 3); + } + } + + /// + /// Mixed int32 + double broadcasting promotes to double. + /// >>> np.array([1, 2, 3]) + np.array([0.5]) + /// array([1.5, 2.5, 3.5]) + /// + [TestMethod] + public void Add_MixedDtypeBroadcast() + { + var i32 = np.array(new int[] { 1, 2, 3 }); + var f64 = np.array(new double[] { 0.5 }); + var result = i32 + f64; + + Assert.AreEqual(NPTypeCode.Double, result.typecode); + AssertShapeEqual(result, 3); + Assert.AreEqual(1.5, result.GetDouble(0)); + Assert.AreEqual(2.5, result.GetDouble(1)); + Assert.AreEqual(3.5, result.GetDouble(2)); + } + + /// + /// Negative value arithmetic with broadcasting. + /// >>> np.array([-3,-2,-1,0,1,2,3]) + np.array([[10],[-10]]) + /// array([[ 7, 8, 9, 10, 11, 12, 13], + /// [-13, -12, -11, -10, -9, -8, -7]]) + /// + [TestMethod] + public void Add_NegativeValuesBroadcast() + { + var a = np.array(new int[] { -3, -2, -1, 0, 1, 2, 3 }); + var b = np.array(new int[,] { { 10 }, { -10 } }); + var result = a + b; + + AssertShapeEqual(result, 2, 7); + Assert.AreEqual(7, result.GetInt32(0, 0)); + Assert.AreEqual(13, result.GetInt32(0, 6)); + Assert.AreEqual(-13, result.GetInt32(1, 0)); + Assert.AreEqual(-7, result.GetInt32(1, 6)); + } + + /// + /// np.maximum with broadcasting. + /// >>> np.maximum(np.array([1, 5, 3]), np.array([[2], [4]])) + /// array([[2, 5, 3], + /// [4, 5, 4]]) + /// + [TestMethod] + public void Maximum_Broadcast() + { + var a = np.array(new int[] { 1, 5, 3 }); + var b = np.array(new int[,] { { 2 }, { 4 } }); + var mx = np.maximum(a, b); + + AssertShapeEqual(mx, 2, 3); + Assert.AreEqual(2, mx.GetInt32(0, 0)); + Assert.AreEqual(5, mx.GetInt32(0, 1)); + Assert.AreEqual(3, mx.GetInt32(0, 2)); + Assert.AreEqual(4, mx.GetInt32(1, 0)); + Assert.AreEqual(5, mx.GetInt32(1, 1)); + Assert.AreEqual(4, mx.GetInt32(1, 2)); + } + + /// + /// Sliced columns and rows from the same array broadcast against each other. + /// >>> x = np.arange(20).reshape(4, 5) + /// >>> x[:, 3:4] + x[2:3, :] + /// array([[13, 14, 15, 16, 17], + /// [18, 19, 20, 21, 22], + /// [23, 24, 25, 26, 27], + /// [28, 29, 30, 31, 32]]) + /// + [TestMethod] + public void Add_SlicedColPlusSlicedRow() + { + var x = np.arange(20).reshape(4, 5); + var col = x[":, 3:4"]; // (4,1): [[3],[8],[13],[18]] + var row = x["2:3, :"]; // (1,5): [[10,11,12,13,14]] + var result = col + row; + + AssertShapeEqual(result, 4, 5); + Assert.AreEqual(13, result.GetInt32(0, 0)); // 3+10 + Assert.AreEqual(17, result.GetInt32(0, 4)); // 3+14 + Assert.AreEqual(28, result.GetInt32(3, 0)); // 18+10 + Assert.AreEqual(32, result.GetInt32(3, 4)); // 18+14 + } + + /// + /// broadcast_to result can be sliced, and sliced values are correct. + /// >>> x = np.array([10, 20, 30]) + /// >>> y = np.broadcast_to(x, (4, 3)) + /// >>> y[1:3, :] + /// array([[10, 20, 30], + /// [10, 20, 30]]) + /// + [TestMethod] + public void BroadcastTo_ThenSlice() + { + var x = np.array(new int[] { 10, 20, 30 }); + var bx = np.broadcast_to(x, new Shape(4, 3)); + var sliced = bx["1:3, :"]; + + AssertShapeEqual(sliced, 2, 3); + Assert.AreEqual(10, sliced.GetInt32(0, 0)); + Assert.AreEqual(30, sliced.GetInt32(0, 2)); + Assert.AreEqual(10, sliced.GetInt32(1, 0)); + Assert.AreEqual(30, sliced.GetInt32(1, 2)); + } + + /// + /// broadcast_to result can be integer-indexed. + /// >>> y = np.broadcast_to(np.array([10, 20, 30]), (4, 3)) + /// >>> y[0] + /// array([10, 20, 30]) + /// >>> y[-1] + /// array([10, 20, 30]) + /// + [TestMethod] + public void BroadcastTo_ThenIntegerIndex() + { + var bx = np.broadcast_to(np.array(new int[] { 10, 20, 30 }), new Shape(4, 3)); + + var row0 = bx["0"]; + AssertShapeEqual(row0, 3); + Assert.AreEqual(10, row0.GetInt32(0)); + Assert.AreEqual(20, row0.GetInt32(1)); + Assert.AreEqual(30, row0.GetInt32(2)); + + var rowLast = bx["-1"]; + Assert.AreEqual(10, rowLast.GetInt32(0)); + Assert.AreEqual(30, rowLast.GetInt32(2)); + } + + /// + /// Broadcast result + transpose + reshape chain. + /// >>> c = np.arange(3).reshape(1, 3) + np.arange(3).reshape(3, 1) + /// >>> np.transpose(c) + /// array([[0, 1, 2], + /// [1, 2, 3], + /// [2, 3, 4]]) + /// + [TestMethod] + public void BroadcastResult_Transpose() + { + var c = np.arange(3).reshape(1, 3) + np.arange(3).reshape(3, 1); + // c is symmetric: [[0,1,2],[1,2,3],[2,3,4]] + var t = np.transpose(c); + + AssertShapeEqual(t, 3, 3); + Assert.AreEqual(1, t.GetInt32(0, 1)); + Assert.AreEqual(1, t.GetInt32(1, 0)); + Assert.AreEqual(4, t.GetInt32(2, 2)); + } + + /// + /// Broadcast result can be reshaped. + /// >>> c = np.arange(6).reshape(2, 3) + np.array([1, 2, 3]) + /// >>> c.reshape(3, 2) + /// array([[1, 3], + /// [5, 4], + /// [6, 8]]) + /// + [TestMethod] + public void BroadcastResult_Reshape() + { + var c = np.arange(6).reshape(2, 3) + np.array(new int[] { 1, 2, 3 }); + // c = [[1,3,5],[4,6,8]] + var r = c.reshape(3, 2); + + AssertShapeEqual(r, 3, 2); + Assert.AreEqual(1, r.GetInt32(0, 0)); + Assert.AreEqual(3, r.GetInt32(0, 1)); + Assert.AreEqual(5, r.GetInt32(1, 0)); + Assert.AreEqual(4, r.GetInt32(1, 1)); + Assert.AreEqual(6, r.GetInt32(2, 0)); + Assert.AreEqual(8, r.GetInt32(2, 1)); + } + + /// + /// sum with keepdims on broadcast result. + /// >>> c = np.ones((1, 3)) + np.ones((4, 1)) + /// >>> np.sum(c, axis=0, keepdims=True) + /// array([[8., 8., 8.]]) + /// + [TestMethod] + public void Sum_Keepdims_BroadcastResult() + { + var c = np.ones(new Shape(1, 3)) + np.ones(new Shape(4, 1)); + var s = np.sum(c, axis: 0, keepdims: true); + + AssertShapeEqual(s, 1, 3); + Assert.AreEqual(8.0, s.GetDouble(0, 0)); + Assert.AreEqual(8.0, s.GetDouble(0, 1)); + Assert.AreEqual(8.0, s.GetDouble(0, 2)); + } + + /// + /// np.mean on broadcast result. + /// >>> c = np.arange(3).reshape(1, 3) + np.ones((4, 1), dtype=int) * 10 + /// >>> np.mean(c) + /// 11.0 + /// + [TestMethod] + public void Mean_BroadcastResult() + { + var c = np.arange(3).reshape(1, 3) + np.ones(new Shape(4, 1), NPTypeCode.Int32) * 10; + // c = [[10,11,12],[10,11,12],[10,11,12],[10,11,12]] + var m = np.mean(c); + + Assert.AreEqual(11.0, m.GetDouble(0), 0.001); + } + + /// + /// Scalar + scalar via broadcasting. + /// >>> np.int32(5) + np.int32(3) + /// 8 + /// + [TestMethod] + public void Add_ScalarPlusScalar() + { + var c = NDArray.Scalar(5) + NDArray.Scalar(3); + Assert.AreEqual(8, c.GetInt32(0)); + } + + /// + /// 7D broadcasting. + /// >>> (np.ones((1,2,1,3,1,4,1)) + np.ones((5,1,6,1,7,1,8))).shape + /// (5, 2, 6, 3, 7, 4, 8) + /// + [TestMethod] + public void Broadcast_7D() + { + var a = np.ones(new Shape(1, 2, 1, 3, 1, 4, 1)); + var b = np.ones(new Shape(5, 1, 6, 1, 7, 1, 8)); + var c = a + b; + + AssertShapeEqual(c, 5, 2, 6, 3, 7, 4, 8); + Assert.AreEqual(2.0, c.GetDouble(0, 0, 0, 0, 0, 0, 0)); + } + + /// + /// Unary negation on broadcast result. + /// >>> -( np.arange(3).reshape(1, 3) + np.arange(2).reshape(2, 1) ) + /// array([[ 0, -1, -2], + /// [-1, -2, -3]]) + /// + [TestMethod] + public void UnaryMinus_BroadcastResult() + { + var c = np.arange(3).reshape(1, 3) + np.arange(2).reshape(2, 1); + var neg = -c; + + AssertShapeEqual(neg, 2, 3); + Assert.AreEqual(0, neg.GetInt32(0, 0)); + Assert.AreEqual(-2, neg.GetInt32(0, 2)); + Assert.AreEqual(-1, neg.GetInt32(1, 0)); + Assert.AreEqual(-3, neg.GetInt32(1, 2)); + } + + /// + /// np.sqrt on broadcast result. + /// >>> np.sqrt(np.array([1., 4., 9.]) + np.zeros((2, 1))) + /// array([[1., 2., 3.], + /// [1., 2., 3.]]) + /// + [TestMethod] + public void Sqrt_BroadcastResult() + { + var c = np.array(new double[] { 1.0, 4.0, 9.0 }) + np.zeros(new Shape(2, 1)); + var s = np.sqrt(c); + + AssertShapeEqual(s, 2, 3); + Assert.AreEqual(1.0, s.GetDouble(0, 0), 0.001); + Assert.AreEqual(2.0, s.GetDouble(0, 1), 0.001); + Assert.AreEqual(3.0, s.GetDouble(1, 2), 0.001); + } + + /// + /// Very asymmetric shape broadcasting. + /// >>> (np.ones((100, 1)) + np.ones((1, 200))).shape + /// (100, 200) + /// + [TestMethod] + public void Broadcast_LargeAsymmetric() + { + var a = np.ones(new Shape(100, 1)); + var b = np.ones(new Shape(1, 200)); + var c = a + b; + + AssertShapeEqual(c, 100, 200); + Assert.AreEqual(20000, c.size); + Assert.AreEqual(2.0, c.GetDouble(0, 0)); + Assert.AreEqual(2.0, c.GetDouble(99, 199)); + } + + /// + /// broadcast_to scalar to large 1D shape. + /// >>> np.broadcast_to(np.float64(1.0), (10000,)) + /// All elements should be 1.0. + /// + [TestMethod] + public void BroadcastTo_ScalarToLargeShape() + { + var scalar = np.array(new double[] { 1.0 }); + var b = np.broadcast_to(scalar, new Shape(10000)); + + Assert.AreEqual(10000, b.size); + Assert.AreEqual(1.0, b.GetDouble(0)); + Assert.AreEqual(1.0, b.GetDouble(9999)); + } + + /// + /// Chained operations: broadcast add, then square, then sum. + /// >>> a = np.arange(3).reshape(1, 3) + /// >>> b = np.arange(3).reshape(3, 1) + /// >>> c = a + b + /// >>> d = c * c # element-wise square + /// >>> np.sum(d) + /// 48 + /// + [TestMethod] + public void ChainedBroadcast_AddSquareSum() + { + var a = np.arange(3).reshape(1, 3); + var b = np.arange(3).reshape(3, 1); + var c = a + b; + var d = c * c; + var s = (int)np.sum(d); + + // d = [[0,1,4],[1,4,9],[4,9,16]], sum = 48 + Assert.AreEqual(48, s); + } + + /// + /// Boolean dtype broadcasting. + /// >>> np.broadcast_arrays(np.array([True, False, True]), np.array([[True], [False]])) + /// Results should preserve boolean values across broadcast dimensions. + /// + [TestMethod] + public void BroadcastArrays_BoolDtype() + { + var a = np.array(new bool[] { true, false, true }); + var b = np.array(new bool[,] { { true }, { false } }); + var (ba, bb) = np.broadcast_arrays(a, b); + + AssertShapeEqual(ba, 2, 3); + AssertShapeEqual(bb, 2, 3); + + // ba broadcasts [T,F,T] to both rows + Assert.AreEqual(true, ba.GetBoolean(0, 0)); + Assert.AreEqual(false, ba.GetBoolean(0, 1)); + Assert.AreEqual(true, ba.GetBoolean(1, 0)); + Assert.AreEqual(false, ba.GetBoolean(1, 1)); + + // bb broadcasts [[T],[F]] to all columns + Assert.AreEqual(true, bb.GetBoolean(0, 0)); + Assert.AreEqual(true, bb.GetBoolean(0, 2)); + Assert.AreEqual(false, bb.GetBoolean(1, 0)); + Assert.AreEqual(false, bb.GetBoolean(1, 2)); + } + + #endregion + } +} diff --git a/test/NumSharp.UnitTest/LAPACK/dgels.cs b/test/NumSharp.UnitTest/LAPACK/dgels.cs deleted file mode 100644 index f9345f54e..000000000 --- a/test/NumSharp.UnitTest/LAPACK/dgels.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using NumSharp.Extensions; -using System.Linq; -using System.Numerics; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NumSharp; - -namespace NumSharp.UnitTest.Extensions -{ - /// - /// Test concolve with standard example from - /// https://www.numpy.org/devdocs/reference/generated/numpy.convolve.html - /// - [Ignore] - [TestClass] - public class NdArrayDGELSTest - { - [TestMethod] - public void Standard() - { - double[] a = {1.44, -9.96, -7.55, 8.34, 7.08, -5.45, -7.84, -0.28, 3.24, 8.09, 2.52, -5.70, -4.39, -3.24, 6.27, 5.28, 0.74, -1.19, 4.53, 3.83, -6.64, 2.06, -2.47, 4.70}; - - double[] b = {8.58, 8.26, 8.48, -5.28, 5.72, 8.93, 9.35, -4.43, -0.70, -0.26, -7.36, -2.52}; - int m, n, lda, ldb, nrhs; - - m = 6; - n = 4; - nrhs = 2; - lda = 6; - ldb = 6; - - double dcont = 0.0001; - int rank = 0; - - double[] work = new double[1]; - int lwork = -1; - int info = 0; - double[] singVal = new double[6]; - - LAPACK.dgelss_(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, singVal, ref dcont, ref rank, work, ref lwork, ref info); - - lwork = (int)work[0]; - work = new double[lwork]; - - LAPACK.dgelss_(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, singVal, ref dcont, ref rank, work, ref lwork, ref info); - } - } -} diff --git a/test/NumSharp.UnitTest/LAPACK/dgesvd.cs b/test/NumSharp.UnitTest/LAPACK/dgesvd.cs deleted file mode 100644 index 0008a4f12..000000000 --- a/test/NumSharp.UnitTest/LAPACK/dgesvd.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using NumSharp.Extensions; -using System.Linq; -using System.Numerics; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NumSharp; - -namespace NumSharp.UnitTest.Extensions -{ - /// - /// Test concolve with standard example from - /// https://www.numpy.org/devdocs/reference/generated/numpy.convolve.html - /// - [TestClass] - public class NdArraySVDTest - { - [Ignore] - [TestMethod] - public void Standard() - { - double[] A = {8.79, 6.11, -9.15, 9.57, -3.49, 9.84, 9.93, 6.91, -7.93, 1.64, 4.02, 0.15, 9.83, 5.04, 4.86, 8.83, 9.80, -8.99, 5.45, -0.27, 4.85, 0.74, 10.00, -6.02, 3.16, 7.98, 3.01, 5.80, 4.27, -5.31}; - - int m = 6, n = 5, lda = 6, ldu = 6, ldvt = 5, info = 0, lwork = -1; - - double[] work = new double[5]; - - /* Local arrays */ - double[] s = new double[5], u = new double[6 * 6], vt = new double[5 * 5]; - - LAPACK.dgesvd_("ALL".ToCharArray(), "All".ToCharArray(), ref m, ref n, A, ref lda, s, u, ref ldu, vt, ref ldvt, work, ref lwork, ref info); - - lwork = (int)work[0]; - - work = new double[lwork]; - - LAPACK.dgesvd_("ALL".ToCharArray(), "All".ToCharArray(), ref m, ref n, A, ref lda, s, u, ref ldu, vt, ref ldvt, work, ref lwork, ref info); - } - } -} diff --git a/test/NumSharp.UnitTest/Logic/np.any.Test.cs b/test/NumSharp.UnitTest/Logic/np.any.Test.cs index db11390be..a43020f1a 100644 --- a/test/NumSharp.UnitTest/Logic/np.any.Test.cs +++ b/test/NumSharp.UnitTest/Logic/np.any.Test.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using NumSharp; @@ -13,7 +14,7 @@ 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)); + Assert.AreEqual(true, result.GetBoolean(0)); } [TestMethod] diff --git a/test/NumSharp.UnitTest/NumSharp.Bitmap/BitmapExtensionsTests.cs b/test/NumSharp.UnitTest/NumSharp.Bitmap/BitmapExtensionsTests.cs new file mode 100644 index 000000000..ea92f70f4 --- /dev/null +++ b/test/NumSharp.UnitTest/NumSharp.Bitmap/BitmapExtensionsTests.cs @@ -0,0 +1,665 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NumSharp.UnitTest.Utilities; + +namespace NumSharp.UnitTest +{ + [TestClass] + public class BitmapExtensionsTests : TestClass + { + // ================================================================ + // Bugs discovered during test coverage expansion: + // + // BUG: ToNDArray(copy: true) fails on odd-width images with + // IncorrectShapeException because it copies stride*height bytes + // (which includes padding) then reshapes to (1,H,W,bpp) which + // expects H*W*bpp elements. The copy:false path handles this + // correctly via ReshapeFlatData(). Tests for odd-width use + // copy:false to avoid this. + // + // BUG: AsNDArray(flat:true) crashes with IndexOutOfRangeException + // because it accesses shape[3] on the 1-d flat array before + // reshaping. Tests for AsNDArray(flat:true) are skipped. + // + // BUG: ToBitmap with sliced (non-contiguous) NDArray fails because + // Shape.IsContiguous returns true for the slice shape, but the + // underlying storage buffer is the original (larger) allocation. + // CopyTo then throws because source > destination. + // + // BUG: 24bpp round-trip on narrow widths (e.g., 3 pixels) can + // miscompute bpp as stride/width when stride includes alignment + // padding (e.g., stride=12 for 3px * 3bpp = 9, padded to 12, + // giving 12/3=4 "channels"). Use widths that are multiples of + // 4 to avoid this. + // ================================================================ + + #region ToNDArray — dtype and contiguity + + [TestMethod] + public void ToNDArray_Copy_ReturnsContiguousByteArray() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var nd = bitmap.ToNDArray(copy: true); + nd.Should().BeOfType(NPTypeCode.Byte); + nd.Shape.IsContiguous.Should().BeTrue("copy mode should produce contiguous memory"); + } + + [TestMethod] + public void ToNDArray_NoCopy_ReturnsByteArray() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var nd = bitmap.ToNDArray(copy: false); + nd.Should().BeOfType(NPTypeCode.Byte); + } + + #endregion + + #region ToNDArray — size consistency + + [TestMethod] + public void ToNDArray_Copy_TotalSizeMatchesDimensions() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var nd = bitmap.ToNDArray(copy: true, discardAlpha: false); + nd.size.Should().Be(nd.shape[0] * nd.shape[1] * nd.shape[2] * nd.shape[3]); + } + + [TestMethod] + public void ToNDArray_NoCopy_TotalSizeMatchesDimensions() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var nd = bitmap.ToNDArray(copy: false, discardAlpha: false); + nd.size.Should().Be(nd.shape[0] * nd.shape[1] * nd.shape[2] * nd.shape[3]); + } + + #endregion + + #region ToNDArray — discardAlpha reduces 4th dimension + + [TestMethod] + public void ToNDArray_Copy_DiscardAlpha_Reduces4thDimFrom4To3() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var withAlpha = bitmap.ToNDArray(copy: true, discardAlpha: false); + var bitmap2 = EmbeddedBitmap("captcha-a"); + var noAlpha = bitmap2.ToNDArray(copy: true, discardAlpha: true); + + withAlpha.shape[3].Should().Be(4, "captcha-a is 32bpp ARGB"); + noAlpha.shape[3].Should().Be(3, "discardAlpha should strip alpha channel"); + noAlpha.shape[1].Should().Be(withAlpha.shape[1], "height unchanged"); + noAlpha.shape[2].Should().Be(withAlpha.shape[2], "width unchanged"); + } + + [TestMethod] + public void ToNDArray_NoCopy_DiscardAlpha_Reduces4thDimFrom4To3() + { + // Must use separate bitmaps because copy:false holds the lock + var bitmap1 = EmbeddedBitmap("captcha-a"); + var withAlpha = bitmap1.ToNDArray(copy: false, discardAlpha: false); + var bitmap2 = EmbeddedBitmap("captcha-a"); + var noAlpha = bitmap2.ToNDArray(copy: false, discardAlpha: true); + + withAlpha.shape[3].Should().Be(4); + noAlpha.shape[3].Should().Be(3); + } + + #endregion + + #region ToNDArray — flat output + + [TestMethod] + public void ToNDArray_Flat_Copy_Is1D() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var nd = bitmap.ToNDArray(flat: true, copy: true, discardAlpha: true); + nd.ndim.Should().Be(1, "flat=true should produce 1-d array"); + } + + [TestMethod] + public void ToNDArray_Flat_NoCopy_Is1D() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var nd = bitmap.ToNDArray(flat: true, copy: false, discardAlpha: true); + nd.ndim.Should().Be(1); + } + + [TestMethod] + public void ToNDArray_Flat_SizeMatchesShaped() + { + var bitmap1 = EmbeddedBitmap("captcha-a"); + var shaped = bitmap1.ToNDArray(flat: false, copy: true, discardAlpha: true); + var bitmap2 = EmbeddedBitmap("captcha-a"); + var flat = bitmap2.ToNDArray(flat: true, copy: true, discardAlpha: true); + flat.size.Should().Be(shaped.size, "flat and shaped should have same total elements"); + } + + #endregion + + #region ToNDArray — pixel data correctness + + [TestMethod] + public void ToNDArray_Copy_And_NoCopy_ProduceSameData() + { + var bitmap1 = EmbeddedBitmap("captcha-a"); + var copied = bitmap1.ToNDArray(copy: true, discardAlpha: false); + var bitmap2 = EmbeddedBitmap("captcha-a"); + var wrapped = bitmap2.ToNDArray(copy: false, discardAlpha: false); + + copied.Should().BeShaped(wrapped.shape[0], wrapped.shape[1], wrapped.shape[2], wrapped.shape[3]); + + // Compare first row of pixels + var row1_copy = copied["0, 0, :, :"].flat; + var row1_wrap = wrapped["0, 0, :, :"].flat; + np.array_equal(row1_copy, row1_wrap).Should().BeTrue("first row should be identical between copy and no-copy"); + + // Compare last row + var lastRow = copied.shape[1] - 1; + var rowN_copy = copied[$"0, {lastRow}, :, :"].flat; + var rowN_wrap = wrapped[$"0, {lastRow}, :, :"].flat; + np.array_equal(rowN_copy, rowN_wrap).Should().BeTrue("last row should be identical between copy and no-copy"); + } + + [TestMethod] + public void ToNDArray_PixelValues_AreInByteRange() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var nd = bitmap.ToNDArray(copy: true, discardAlpha: true, flat: true); + var max = np.amax(nd).GetByte(); + var min = np.amin(nd).GetByte(); + max.Should().BeLessOrEqualTo(255); + min.Should().BeGreaterOrEqualTo((byte)0); + } + + [TestMethod] + public void ToNDArray_DiscardAlpha_PixelDataMatchesFirstThreeChannels() + { + var bitmap1 = EmbeddedBitmap("captcha-a"); + var full = bitmap1.ToNDArray(copy: true, discardAlpha: false); + var bitmap2 = EmbeddedBitmap("captcha-a"); + var trimmed = bitmap2.ToNDArray(copy: true, discardAlpha: true); + + // The trimmed array should match the first 3 channels of the full array + var fullRgb = full[Slice.All, Slice.All, Slice.All, new Slice(stop: 3)]; + np.array_equal(trimmed, fullRgb).Should().BeTrue("discardAlpha should be equivalent to slicing [:,:,:,:3]"); + } + + [TestMethod] + public void ToNDArray_NotAllZeros() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var nd = bitmap.ToNDArray(copy: true, flat: true); + var sum = np.sum(nd.astype(NPTypeCode.Int64)); + ((long)sum).Should().BeGreaterThan(0, "a real image should have non-zero pixel data"); + } + + #endregion + + #region ToNDArray — odd width (stride padding) + + [TestMethod] + public void ToNDArray_OddWidth_NoCopy_ShapeMatchesBitmap() + { + // copy:false uses ReshapeFlatData which handles stride padding correctly + var bitmap = EmbeddedBitmap("odd-width"); + var nd = bitmap.ToNDArray(copy: false, discardAlpha: false); + nd.shape[1].Should().Be(bitmap.Height); + nd.shape[2].Should().Be(bitmap.Width); + } + + [TestMethod] + public void ToNDArray_OddWidth_NoCopy_Flat_Is1D() + { + var bitmap = EmbeddedBitmap("odd-width"); + var nd = bitmap.ToNDArray(copy: false, discardAlpha: false, flat: true); + nd.ndim.Should().Be(1); + nd.size.Should().BeGreaterThan(0); + } + + #endregion + + #region ToBitmap — round-trip with 32bpp (no stride padding issues) + + [TestMethod] + public void ToBitmap_RoundTrip_32bpp_PreservesPixelData() + { + // 32bpp ARGB: 4 bytes per pixel, stride is always width*4 (no padding) + var pixels = np.array(new byte[] { + 255, 0, 0, 128, 0, 255, 0, 255, + 0, 0, 255, 64, 128, 128, 128, 0 + }).reshape(1, 2, 2, 4); + + var bmp = pixels.ToBitmap(2, 2, PixelFormat.Format32bppArgb); + bmp.Width.Should().Be(2); + bmp.Height.Should().Be(2); + + var recovered = bmp.ToNDArray(copy: true, discardAlpha: false); + recovered.Should().BeShaped(1, 2, 2, 4); + np.array_equal(pixels, recovered).Should().BeTrue("32bpp round-trip should preserve all channels including alpha"); + } + + [TestMethod] + public void ToBitmap_RoundTrip_24bpp_EvenWidth() + { + // Use width=4 (multiple of 4) to avoid stride padding issues + var pixels = np.arange(0, 4 * 2 * 3).reshape(1, 2, 4, 3).astype(NPTypeCode.Byte); + + var bmp = pixels.ToBitmap(4, 2, PixelFormat.Format24bppRgb); + bmp.Width.Should().Be(4); + bmp.Height.Should().Be(2); + bmp.PixelFormat.Should().Be(PixelFormat.Format24bppRgb); + + var recovered = bmp.ToNDArray(copy: true, discardAlpha: false); + recovered.Should().BeShaped(1, 2, 4, 3); + np.array_equal(pixels, recovered).Should().BeTrue("24bpp round-trip with even width should preserve data"); + } + + [TestMethod] + public void ToBitmap_RoundTrip_FromEmbedded_32bpp() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var nd = bitmap.ToNDArray(copy: true, discardAlpha: false); + var bmp2 = nd.ToBitmap(); + bmp2.Width.Should().Be(bitmap.Width); + bmp2.Height.Should().Be(bitmap.Height); + + // Round-trip back and compare + var nd2 = bmp2.ToNDArray(copy: true, discardAlpha: false); + nd2.Should().BeShaped(nd.shape[0], nd.shape[1], nd.shape[2], nd.shape[3]); + np.array_equal(nd, nd2).Should().BeTrue("embedded image round-trip should be lossless"); + } + + [TestMethod] + public void ToBitmap_RoundTrip_OddWidth_NoCopy() + { + // Use copy:false to avoid stride padding bug in copy path + var bitmap = EmbeddedBitmap("odd-width"); + var nd = bitmap.ToNDArray(copy: false, discardAlpha: false); + var bmp2 = nd.ToBitmap(); + bmp2.Width.Should().Be(bitmap.Width); + bmp2.Height.Should().Be(bitmap.Height); + } + + #endregion + + #region ToBitmap — auto format detection + + [TestMethod] + public void ToBitmap_DontCare_3Channel_Infers24bpp() + { + var nd = np.zeros(1, 4, 4, 3).astype(NPTypeCode.Byte); + var bmp = nd.ToBitmap(); + bmp.PixelFormat.Should().Be(PixelFormat.Format24bppRgb); + } + + [TestMethod] + public void ToBitmap_DontCare_4Channel_Infers32bpp() + { + var nd = np.zeros(1, 4, 4, 4).astype(NPTypeCode.Byte); + var bmp = nd.ToBitmap(); + bmp.PixelFormat.Should().Be(PixelFormat.Format32bppArgb); + } + + [TestMethod] + public void ToBitmap_WithExplicitFormat_Uses24bpp() + { + // Use 4-pixel width to avoid stride padding + var nd = np.arange(0, 4 * 3 * 3).reshape(1, 3, 4, 3).astype(NPTypeCode.Byte); + var bmp = nd.ToBitmap(4, 3, PixelFormat.Format24bppRgb); + bmp.PixelFormat.Should().Be(PixelFormat.Format24bppRgb); + bmp.Width.Should().Be(4); + bmp.Height.Should().Be(3); + } + + [TestMethod] + public void ToBitmap_WithExplicitFormat_Uses32bpp() + { + var nd = np.arange(0, 3 * 3 * 4).reshape(1, 3, 3, 4).astype(NPTypeCode.Byte); + var bmp = nd.ToBitmap(3, 3, PixelFormat.Format32bppArgb); + bmp.PixelFormat.Should().Be(PixelFormat.Format32bppArgb); + } + + #endregion + + #region ToBitmap — flat input with explicit format + + [TestMethod] + public void ToBitmap_FlatInput_WithFormat_ReshapesCorrectly() + { + // 4x3 image, 32bpp = 48 bytes (no stride padding for 32bpp) + var flat = np.arange(0, 4 * 3 * 4).astype(NPTypeCode.Byte); + flat.ndim.Should().Be(1); + var bmp = flat.ToBitmap(4, 3, PixelFormat.Format32bppArgb); + bmp.Width.Should().Be(4); + bmp.Height.Should().Be(3); + bmp.PixelFormat.Should().Be(PixelFormat.Format32bppArgb); + } + + #endregion + + #region ToBitmap — error handling + + [TestMethod] + public void ToBitmap_NullNDArray_ThrowsArgumentNull() + { + NDArray nd = null; + Action act = () => nd.ToBitmap(); + act.Should().Throw(); + } + + [TestMethod] + public void ToBitmap_WrongNdim_ThrowsArgumentException() + { + var nd = np.zeros(3, 3, 3).astype(NPTypeCode.Byte); + Action act = () => nd.ToBitmap(); + act.Should().Throw(); + } + + [TestMethod] + public void ToBitmap_MultiplePictures_ThrowsArgumentException() + { + var nd = np.zeros(2, 3, 3, 3).astype(NPTypeCode.Byte); + Action act = () => nd.ToBitmap(); + act.Should().Throw(); + } + + [TestMethod] + public void ToBitmap_FormatMismatch_ThrowsArgumentException() + { + // 3-channel data but requesting 32bpp (4 channels) + var nd = np.zeros(1, 3, 3, 3).astype(NPTypeCode.Byte); + Action act = () => nd.ToBitmap(3, 3, PixelFormat.Format32bppArgb); + act.Should().Throw(); + } + + #endregion + + #region ToNDArray — error handling + + [TestMethod] + public void ToNDArray_NullBitmap_ThrowsArgumentNull() + { + Bitmap bmp = null; + Action act = () => bmp.ToNDArray(); + act.Should().Throw(); + } + + [TestMethod] + public void ToNDArray_NullImage_ThrowsArgumentNull() + { + Image img = null; + Action act = () => img.ToNDArray(); + act.Should().Throw(); + } + + #endregion + + #region Image.ToNDArray — delegates to Bitmap + + [TestMethod] + public void ImageToNDArray_ProducesSameResultAsBitmapToNDArray() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var fromBitmap = bitmap.ToNDArray(copy: true, discardAlpha: false); + + Image image = EmbeddedBitmap("captcha-a"); + var fromImage = image.ToNDArray(copy: true, discardAlpha: false); + + fromImage.Should().BeShaped(fromBitmap.shape[0], fromBitmap.shape[1], fromBitmap.shape[2], fromBitmap.shape[3]); + np.array_equal(fromBitmap, fromImage).Should().BeTrue("Image.ToNDArray should produce same data as Bitmap.ToNDArray"); + } + + #endregion + + #region AsNDArray — BitmapData wrapper + + [TestMethod] + public unsafe void AsNDArray_WrapsLockBitsWithoutCopy() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var bmpData = bitmap.LockBits( + new Rectangle(0, 0, bitmap.Width, bitmap.Height), + ImageLockMode.ReadOnly, + bitmap.PixelFormat); + + try + { + var nd = bmpData.AsNDArray(flat: false, discardAlpha: false); + nd.Should().BeShaped(1, bitmap.Height, bitmap.Width, 4); + nd.Should().BeOfType(NPTypeCode.Byte); + nd.size.Should().Be(bitmap.Height * bitmap.Width * 4); + } + finally + { + bitmap.UnlockBits(bmpData); + } + } + + [TestMethod] + public unsafe void AsNDArray_Shaped_DiscardAlpha() + { + var bitmap = EmbeddedBitmap("captcha-a"); + var bmpData = bitmap.LockBits( + new Rectangle(0, 0, bitmap.Width, bitmap.Height), + ImageLockMode.ReadOnly, + bitmap.PixelFormat); + + try + { + var nd = bmpData.AsNDArray(flat: false, discardAlpha: true); + nd.Should().BeShaped(1, bitmap.Height, bitmap.Width, 3); + } + finally + { + bitmap.UnlockBits(bmpData); + } + } + + [TestMethod] + public void AsNDArray_NullBitmapData_ThrowsArgumentNull() + { + BitmapData bmpData = null; + Action act = () => bmpData.AsNDArray(); + act.Should().Throw(); + } + + #endregion + + #region ToBytesPerPixel + + [TestMethod] + public void ToBytesPerPixel_24bppRgb_Returns3() + { + PixelFormat.Format24bppRgb.ToBytesPerPixel().Should().Be(3); + } + + [TestMethod] + public void ToBytesPerPixel_32bppArgb_Returns4() + { + PixelFormat.Format32bppArgb.ToBytesPerPixel().Should().Be(4); + } + + [TestMethod] + public void ToBytesPerPixel_32bppPArgb_Returns4() + { + PixelFormat.Format32bppPArgb.ToBytesPerPixel().Should().Be(4); + } + + [TestMethod] + public void ToBytesPerPixel_32bppRgb_Returns4() + { + PixelFormat.Format32bppRgb.ToBytesPerPixel().Should().Be(4); + } + + [TestMethod] + public void ToBytesPerPixel_48bppRgb_Returns6() + { + PixelFormat.Format48bppRgb.ToBytesPerPixel().Should().Be(6); + } + + [TestMethod] + public void ToBytesPerPixel_64bppArgb_Returns8() + { + PixelFormat.Format64bppArgb.ToBytesPerPixel().Should().Be(8); + } + + [TestMethod] + public void ToBytesPerPixel_64bppPArgb_Returns8() + { + PixelFormat.Format64bppPArgb.ToBytesPerPixel().Should().Be(8); + } + + [TestMethod] + public void ToBytesPerPixel_16bppFormats_Return2() + { + PixelFormat.Format16bppGrayScale.ToBytesPerPixel().Should().Be(2); + PixelFormat.Format16bppRgb555.ToBytesPerPixel().Should().Be(2); + PixelFormat.Format16bppRgb565.ToBytesPerPixel().Should().Be(2); + PixelFormat.Format16bppArgb1555.ToBytesPerPixel().Should().Be(2); + } + + [TestMethod] + public void ToBytesPerPixel_IndexedFormat_ThrowsArgumentException() + { + Action act = () => PixelFormat.Format8bppIndexed.ToBytesPerPixel(); + act.Should().Throw(); + } + + [TestMethod] + public void ToBytesPerPixel_DontCare_ThrowsArgumentException() + { + Action act = () => PixelFormat.DontCare.ToBytesPerPixel(); + act.Should().Throw(); + } + + #endregion + + #region Multiple embedded resources + + [TestMethod] + public void ToNDArray_DifferentImages_HaveDifferentShapes() + { + var captcha = EmbeddedBitmap("captcha-a"); + var ndCaptcha = captcha.ToNDArray(copy: true); + + // Use copy:false for odd-width to avoid stride bug + var odd = EmbeddedBitmap("odd-width"); + var ndOdd = odd.ToNDArray(copy: false); + + // Both should be 4-d with batch=1 + ndCaptcha.ndim.Should().Be(4); + ndOdd.ndim.Should().Be(4); + ndCaptcha.shape[0].Should().Be(1); + ndOdd.shape[0].Should().Be(1); + + // Dimensions should match their source bitmaps + ndCaptcha.shape[1].Should().Be(captcha.Height); + ndCaptcha.shape[2].Should().Be(captcha.Width); + ndOdd.shape[1].Should().Be(odd.Height); + ndOdd.shape[2].Should().Be(odd.Width); + + // They should be different sizes + (ndCaptcha.shape[1] == ndOdd.shape[1] && ndCaptcha.shape[2] == ndOdd.shape[2]) + .Should().BeFalse("different images should have different dimensions"); + } + + [TestMethod] + public void ToNDArray_AllCaptchaImages_Load() + { + // Verify all 4 captcha images can be loaded and converted + foreach (var name in new[] { "captcha-a", "captcha-b", "captcha-c", "captcha-d" }) + { + var bitmap = EmbeddedBitmap(name); + bitmap.Should().NotBeNull($"{name} should be loadable"); + + var nd = bitmap.ToNDArray(copy: true); + nd.ndim.Should().Be(4, $"{name} should produce 4-d array"); + nd.shape[0].Should().Be(1, $"{name} batch dim should be 1"); + nd.size.Should().BeGreaterThan(0, $"{name} should have pixel data"); + } + } + + #endregion + + #region Zeros and ones images + + [TestMethod] + public void ToBitmap_AllBlack_RoundTripsCorrectly() + { + var black = np.zeros(1, 8, 8, 3).astype(NPTypeCode.Byte); + var bmp = black.ToBitmap(8, 8, PixelFormat.Format24bppRgb); + var recovered = bmp.ToNDArray(copy: true, discardAlpha: false); + recovered.Should().AllValuesBe((byte)0); + } + + [TestMethod] + public void ToBitmap_AllWhite_RoundTripsCorrectly() + { + var white = (np.zeros(1, 8, 8, 3) + 255).astype(NPTypeCode.Byte); + var bmp = white.ToBitmap(8, 8, PixelFormat.Format24bppRgb); + var recovered = bmp.ToNDArray(copy: true, discardAlpha: false); + recovered.Should().AllValuesBe((byte)255); + } + + [TestMethod] + public void ToBitmap_32bpp_AllBlack_RoundTripsCorrectly() + { + var black = np.zeros(1, 8, 8, 4).astype(NPTypeCode.Byte); + var bmp = black.ToBitmap(8, 8, PixelFormat.Format32bppArgb); + var recovered = bmp.ToNDArray(copy: true, discardAlpha: false); + recovered.Should().BeShaped(1, 8, 8, 4); + recovered.Should().AllValuesBe((byte)0); + } + + [TestMethod] + public void ToBitmap_32bpp_AllWhite_RoundTripsCorrectly() + { + var white = (np.zeros(1, 8, 8, 4) + 255).astype(NPTypeCode.Byte); + var bmp = white.ToBitmap(8, 8, PixelFormat.Format32bppArgb); + var recovered = bmp.ToNDArray(copy: true, discardAlpha: false); + recovered.Should().BeShaped(1, 8, 8, 4); + recovered.Should().AllValuesBe((byte)255); + } + + #endregion + + #region ToBitmap — specific pixel values + + [TestMethod] + public void ToBitmap_32bpp_SpecificPixels_RoundTrip() + { + // Create a 2x2 image with known BGRA values + var pixels = np.array(new byte[] { + 10, 20, 30, 40, 50, 60, 70, 80, + 90, 100, 110, 120, 130, 140, 150, 160 + }).reshape(1, 2, 2, 4); + + var bmp = pixels.ToBitmap(2, 2, PixelFormat.Format32bppArgb); + var recovered = bmp.ToNDArray(copy: true, discardAlpha: false); + + // Verify each pixel exactly + for (int y = 0; y < 2; y++) + for (int x = 0; x < 2; x++) + for (int c = 0; c < 4; c++) + { + byte expected = pixels[$"0, {y}, {x}, {c}"].GetByte(); + byte actual = recovered[$"0, {y}, {x}, {c}"].GetByte(); + actual.Should().Be(expected, $"pixel [{y},{x}] channel {c} should match"); + } + } + + [TestMethod] + public void ToBitmap_SizeProperty_MatchesDimensions() + { + var nd = np.zeros(1, 10, 20, 4).astype(NPTypeCode.Byte); + var bmp = nd.ToBitmap(); + bmp.Width.Should().Be(20); + bmp.Height.Should().Be(10); + bmp.Size.Should().Be(new Size(20, 10)); + } + + #endregion + } +} diff --git a/test/NumSharp.UnitTest/NumSharp.UnitTest.csproj b/test/NumSharp.UnitTest/NumSharp.UnitTest.csproj index 9dabd141f..03a8f5987 100644 --- a/test/NumSharp.UnitTest/NumSharp.UnitTest.csproj +++ b/test/NumSharp.UnitTest/NumSharp.UnitTest.csproj @@ -1,10 +1,10 @@  - net8.0 + net8.0;net10.0 false true - 12.0 + latest AnyCPU Open.snk Debug;Release;Publish @@ -37,8 +37,7 @@ - - + diff --git a/test/NumSharp.UnitTest/OpenBugs.Bitmap.cs b/test/NumSharp.UnitTest/OpenBugs.Bitmap.cs new file mode 100644 index 000000000..227409656 --- /dev/null +++ b/test/NumSharp.UnitTest/OpenBugs.Bitmap.cs @@ -0,0 +1,480 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Drawing; +using System.Drawing.Imaging; +using FluentAssertions; +using NumSharp; +using NumSharp.Backends; +using NumSharp.UnitTest.Utilities; + +namespace NumSharp.UnitTest +{ + /// + /// Open bugs in the NumSharp.Bitmap extension library (np_.extensions.cs). + /// + /// Each test asserts the CORRECT expected behavior. + /// Tests FAIL while the bug exists, and PASS when the bug is fixed. + /// + /// When a bug is fixed, the test starts passing. Move passing tests to + /// BitmapExtensionsTests.cs. + /// + /// ===================================================================== + /// ROOT CAUSE ANALYSIS + /// ===================================================================== + /// + /// The majority of Bitmap bugs stem from TWO architectural problems: + /// + /// 1. STRIDE PADDING NOT HANDLED IN COPY PATH + /// GDI+ aligns bitmap scan lines to 4-byte boundaries, so + /// stride >= width * bytesPerPixel. The no-copy path uses + /// ReshapeFlatData() which correctly handles stride != width * bpp + /// by reshaping to strideWidth then slicing. But the copy path + /// (lines 44-54) copies stride*height bytes then reshapes using + /// stride/width as bpp — which is WRONG when stride includes + /// padding. For a 3px wide 24bpp image: stride=12, width=3, + /// stride/width=4, so it reshapes as 4-channel instead of 3. + /// + /// 2. ToBitmap STRIDE PADDING CONCAT LOGIC IS BROKEN + /// When bitdata.Stride != width * bpp (line 242), ToBitmap tries + /// to fix it by concatenating a zeros column. But this changes + /// nd.size while the earlier size check (line 237) already passed + /// for the original size. The concat also doesn't match the actual + /// stride padding needed — GDI+ pads to 4-byte boundaries per row, + /// not by adding a whole extra pixel column. + /// + /// Bug categories: + /// Bugs 1, 2: Stride padding not handled in copy path (ToNDArray) + /// Bug 3: AsNDArray accesses shape[3] before reshaping flat array + /// Bug 4: ToBitmap non-contiguous sliced array fails in MultiIterator + /// Bug 5: ToBitmap no dtype validation (only byte arrays work) + /// Bugs 6, 7: ToBitmap stride padding concat logic crashes + /// Bug 8: ToBitmap 24bpp round-trip with even narrow widths corrupts data + /// + /// GitHub issues: #396, #440, #475, #491 + /// + /// Total: 8 distinct bugs, 10 test methods. + /// + [TestClass] + public class OpenBugsBitmap : TestClass + { + // ================================================================ + // + // BUG 1: ToNDArray(copy:true) on odd-width 24bpp images produces + // wrong shape — treats stride padding as extra channels + // + // SEVERITY: High — silently returns wrong shape and data layout. + // + // GITHUB: #396 (Bitmap.ToNDArray problem with odd bitmap width) + // + // ROOT CAUSE: The copy path (line 54 of np_.extensions.cs) computes + // bytes-per-pixel as `bmpData.Stride / bmpData.Width`. For a 3px + // wide 24bpp image: stride=12 (9 bytes padded to 4-byte boundary), + // so stride/width = 12/3 = 4. The reshape then creates shape + // (1, H, W, 4) instead of (1, H, W, 3). The no-copy path uses + // ReshapeFlatData() which handles this correctly. + // + // For wider odd-width images (e.g. 227px), the stride/width division + // truncates to 2 instead of 3, causing the reshape to fail entirely + // with IncorrectShapeException because stride*height bytes don't + // divide evenly into (1, H, W, 2). + // + // VERIFICATION: + // 3px wide 24bpp: stride=12, stride/width=4, shape becomes (1,H,3,4) + // 227px wide 24bpp: stride=684, stride/width=3 (happens to work) + // 5px wide 24bpp: stride=16, stride/width=3 (but 16*H != H*5*3) + // + // ================================================================ + + /// + /// BUG 1a: ToNDArray(copy:true) on 3px-wide 24bpp image. + /// stride=12, width=3, stride/width=4 → shape (1,2,3,4) instead of (1,2,3,3). + /// The image has 3 channels (RGB) but gets reshaped as 4 channels. + /// + [TestMethod] + public void Bug1a_ToNDArray_CopyTrue_OddWidth24bpp_WrongShape() + { + var bmp = new Bitmap(3, 2, PixelFormat.Format24bppRgb); + bmp.SetPixel(0, 0, Color.FromArgb(10, 20, 30)); + bmp.SetPixel(1, 0, Color.FromArgb(40, 50, 60)); + bmp.SetPixel(2, 0, Color.FromArgb(70, 80, 90)); + + var nd = bmp.ToNDArray(copy: true, discardAlpha: false); + + // Expected: shape should be (1, 2, 3, 3) for 24bpp RGB + // Actual: shape is (1, 2, 3, 4) because stride/width = 12/3 = 4 + nd.shape[3].Should().Be(3, + "a 24bpp RGB image has 3 bytes per pixel, not 4. " + + "The copy path uses stride/width which includes padding bytes."); + } + + /// + /// BUG 1b: ToNDArray(copy:true) on 5px-wide 24bpp image. + /// stride=16 (5*3=15 padded to 16), width=5, stride/width=3. + /// The division happens to give 3 (correct bpp), but stride*height=32 + /// does not equal height*width*3=30, so the reshape may produce wrong data. + /// + [TestMethod] + public void Bug1b_ToNDArray_CopyTrue_5pxWide24bpp_ExtraPaddingBytes() + { + var bmp = new Bitmap(5, 2, PixelFormat.Format24bppRgb); + for (int x = 0; x < 5; x++) + { + bmp.SetPixel(x, 0, Color.FromArgb(10 + x, 20 + x, 30 + x)); + bmp.SetPixel(x, 1, Color.FromArgb(50 + x, 60 + x, 70 + x)); + } + + var nd = bmp.ToNDArray(copy: true, discardAlpha: false); + + // stride=16, height=2 → 32 bytes copied. But 1*2*5*3 = 30. + // The reshape(1, 2, 5, 3) should work since stride/width = 16/5 = 3. + // But the NDArray has 32 bytes, and reshape expects 30. + nd.Should().BeShaped(1, 2, 5, 3); + nd.size.Should().Be(30, + "5x2 24bpp image has 30 pixels total (5*2*3), " + + "but copy path copies stride*height=32 bytes including padding."); + } + + // ================================================================ + // + // BUG 2: ToNDArray(copy:true) on 24bpp with even narrow widths + // produces wrong pixel data — stride padding corrupts layout + // + // SEVERITY: High — pixel data is silently wrong. + // + // GITHUB: #440 (ToBitmap has critical issue with 24bpp vertical images) + // + // ROOT CAUSE: Same stride/width issue. For 2px wide 24bpp: + // stride=8 (2*3=6 padded to 8), stride/width=8/2=4. + // The reshape produces (1, H, 2, 4) — 4 channels per pixel when + // there are only 3. The round-trip then fails because ToBitmap + // reads back the wrong channel layout. + // + // ================================================================ + + /// + /// BUG 2: ToNDArray(copy:true) on 2px-wide 24bpp → wrong bpp. + /// stride=8, width=2, stride/width=4 → shape (1,2,2,4) instead of (1,2,2,3). + /// Round-trip pixel values are corrupted because channel boundaries shift. + /// + [TestMethod] + public void Bug2_ToNDArray_CopyTrue_2pxWide24bpp_WrongBpp() + { + var bmp = new Bitmap(2, 2, PixelFormat.Format24bppRgb); + bmp.SetPixel(0, 0, Color.FromArgb(10, 20, 30)); + bmp.SetPixel(1, 0, Color.FromArgb(40, 50, 60)); + bmp.SetPixel(0, 1, Color.FromArgb(70, 80, 90)); + bmp.SetPixel(1, 1, Color.FromArgb(100, 110, 120)); + + var nd = bmp.ToNDArray(copy: true, discardAlpha: false); + + // Expected: 3 channels for 24bpp + // Actual: 4 channels because stride/width = 8/2 = 4 + nd.shape[3].Should().Be(3, + "a 24bpp image has 3 bytes per pixel. The copy path computes " + + "stride/width = 8/2 = 4, treating stride padding as a 4th channel."); + } + + // ================================================================ + // + // BUG 3: AsNDArray(flat:true) crashes with IndexOutOfRangeException + // + // SEVERITY: Medium — crashes instead of returning flat data. + // + // ROOT CAUSE: AsNDArray (line 187-189) checks `ret.shape[3]` to + // decide whether to discard alpha. But at that point, `ret` is a + // flat 1-d array (just wrapped from Scan0), so it only has 1 + // dimension. Accessing shape[3] on a 1-d array throws + // IndexOutOfRangeException. The reshape to 4-d should happen + // BEFORE the shape[3] check, not after. + // + // ================================================================ + + /// + /// BUG 3a: AsNDArray(flat:true) crashes accessing shape[3] on 1-d array. + /// + [TestMethod] + public void Bug3a_AsNDArray_FlatTrue_IndexOutOfRange() + { + var bmp = new Bitmap(4, 4, PixelFormat.Format32bppArgb); + var bmpData = bmp.LockBits( + new Rectangle(0, 0, 4, 4), + ImageLockMode.ReadOnly, + bmp.PixelFormat); + + try + { + var nd = bmpData.AsNDArray(flat: true, discardAlpha: false); + + // Expected: flat 1-d array with all pixel bytes + nd.ndim.Should().Be(1, "flat:true should return a 1-d array"); + nd.size.Should().Be(4 * 4 * 4, "4x4 32bpp = 64 bytes"); + } + finally + { + bmp.UnlockBits(bmpData); + } + } + + /// + /// BUG 3b: AsNDArray(flat:true, discardAlpha:true) crashes the same way. + /// The discardAlpha path also accesses shape[3] before reshaping. + /// + [TestMethod] + public void Bug3b_AsNDArray_FlatTrue_DiscardAlpha_IndexOutOfRange() + { + var bmp = new Bitmap(4, 4, PixelFormat.Format32bppArgb); + var bmpData = bmp.LockBits( + new Rectangle(0, 0, 4, 4), + ImageLockMode.ReadOnly, + bmp.PixelFormat); + + try + { + var nd = bmpData.AsNDArray(flat: true, discardAlpha: true); + + // Expected: flat 1-d array with RGB bytes (alpha discarded) + nd.ndim.Should().Be(1, "flat:true should return a 1-d array"); + nd.size.Should().Be(4 * 4 * 3, "4x4 with alpha discarded = 48 bytes"); + } + finally + { + bmp.UnlockBits(bmpData); + } + } + + // ================================================================ + // + // BUG 4: ToBitmap fails on non-contiguous (sliced) NDArray + // + // SEVERITY: Medium — slicing before ToBitmap always crashes. + // + // GITHUB: #475 (ToBitmap fails if not contiguous because of + // Broadcast mismatch) + // + // ROOT CAUSE: When Shape.IsContiguous is false (line 248), ToBitmap + // falls through to MultiIterator.Assign(). This creates an + // UnmanagedStorage with Shape.Vector(stride*height) and tries to + // broadcast it against the sliced NDArray's storage. The shapes + // don't broadcast: a flat (N,) vector can't broadcast against a + // (1, H, W, C) array, causing IncorrectShapeException. + // + // The fix should either copy the sliced NDArray to contiguous memory + // first, or use an iterator that doesn't require broadcasting. + // + // ================================================================ + + /// + /// BUG 4: ToBitmap with sliced (non-contiguous) NDArray throws + /// IncorrectShapeException from MultiIterator broadcast. + /// + [TestMethod] + public void Bug4_ToBitmap_SlicedNDArray_BroadcastMismatch() + { + var full = np.arange(0, 4 * 4 * 4).reshape(1, 4, 4, 4).astype(NPTypeCode.Byte); + var sliced = full[":, :2, :2, :"]; + + sliced.shape.Should().BeEquivalentTo(new[] { 1, 2, 2, 4 }); + sliced.Shape.IsContiguous.Should().BeFalse("slicing makes it non-contiguous"); + + // Expected: ToBitmap should handle non-contiguous arrays + // Actual: throws IncorrectShapeException from MultiIterator.Assign + var bmp = sliced.ToBitmap(); + bmp.Width.Should().Be(2); + bmp.Height.Should().Be(2); + } + + // ================================================================ + // + // BUG 5: ToBitmap with non-byte dtype throws InvalidCastException + // + // SEVERITY: Medium — common user error with no helpful message. + // + // GITHUB: #491 (ToBitmap() - datatype mismatch) + // + // ROOT CAUSE: ToBitmap creates an ArraySlice for the + // destination (line 245), then calls nd.CopyTo(dst) which does a + // typed copy. If nd.dtype is int32 (or any non-byte type), the + // CopyTo fails with InvalidCastException. There's no dtype check + // at the top of ToBitmap to give a clear error message, and no + // automatic .astype(NPTypeCode.Byte) conversion. + // + // At minimum, ToBitmap should throw ArgumentException with a + // message saying the NDArray must be of byte dtype. Ideally it + // should auto-convert with .astype(NPTypeCode.Byte). + // + // ================================================================ + + /// + /// BUG 5: ToBitmap with int32 NDArray throws cryptic InvalidCastException + /// instead of a helpful error or auto-converting to byte. + /// + [TestMethod] + public void Bug5_ToBitmap_NonByteDtype_InvalidCastException() + { + var nd = np.arange(0, 1 * 3 * 4 * 3).reshape(1, 3, 4, 3); // int32 + nd.dtype.Should().Be(typeof(int)); + + // Expected: either auto-convert to byte, or throw helpful ArgumentException + // Actual: throws InvalidCastException "Unable to perform CopyTo when T does not match dtype" + Action act = () => nd.ToBitmap(); + act.Should().NotThrow( + "ToBitmap should either auto-convert to byte or throw a clear " + + "ArgumentException explaining that only byte arrays are supported."); + } + + // ================================================================ + // + // BUG 6: ToBitmap crashes on images where stride != width * bpp + // (1px wide, 5px wide, and other widths where GDI+ pads) + // + // SEVERITY: High — many common image sizes crash. + // + // GITHUB: #440 (ToBitmap critical issue with 24bpp vertical images) + // + // ROOT CAUSE: When bitdata.Stride != width * format.ToBytesPerPixel() + // (line 242), ToBitmap tries to "fix" it by concatenating a zeros + // column: np.concatenate((nd, np.zeros(...)), axis=2). This is + // fundamentally wrong: + // + // 1. GDI+ pads rows to 4-byte boundaries, not by adding pixel columns. + // A 5px wide 24bpp row is 15 bytes, padded to 16 — that's 1 byte + // of padding, not 3 bytes (one pixel column). + // + // 2. The concatenation changes nd.size, but the earlier size check + // (line 237) already passed for the original unpadded size. + // + // 3. After concatenation, the CopyTo source (now larger) overflows + // the destination buffer, causing ArgumentOutOfRangeException. + // + // The correct fix: copy row-by-row, writing width*bpp bytes per row + // to a destination with stride bytes per row (padding the gap with zeros). + // + // ================================================================ + + /// + /// BUG 6a: ToBitmap crashes on 1px wide 24bpp image. + /// stride=4 (1*3=3, padded to 4), width*bpp=3 → concat triggers. + /// + [TestMethod] + public void Bug6a_ToBitmap_1pxWide24bpp_StridePaddingCrash() + { + var nd = np.ones(1, 2, 1, 3).astype(NPTypeCode.Byte); + nd.size.Should().Be(6); + + // Expected: creates a 1x2 24bpp bitmap with all-ones pixels + // Actual: ArgumentOutOfRangeException from CopyTo after broken concat + var bmp = nd.ToBitmap(); + bmp.Width.Should().Be(1); + bmp.Height.Should().Be(2); + + var p0 = bmp.GetPixel(0, 0); + var p1 = bmp.GetPixel(0, 1); + // GDI+ stores BGR, so RGB(1,1,1) → GetPixel returns (1,1,1) + p0.B.Should().Be(1); + p1.B.Should().Be(1); + } + + /// + /// BUG 6b: ToBitmap crashes on 5px wide 24bpp image. + /// stride=16 (5*3=15, padded to 16), width*bpp=15 → concat triggers. + /// + [TestMethod] + public void Bug6b_ToBitmap_5pxWide24bpp_StridePaddingCrash() + { + var nd = np.ones(1, 2, 5, 3).astype(NPTypeCode.Byte); + nd.size.Should().Be(30); + + // Expected: creates a 5x2 24bpp bitmap + // Actual: ArgumentOutOfRangeException from CopyTo after broken concat + var bmp = nd.ToBitmap(); + bmp.Width.Should().Be(5); + bmp.Height.Should().Be(2); + + for (int x = 0; x < 5; x++) + { + var p = bmp.GetPixel(x, 0); + p.B.Should().Be(1, $"pixel ({x},0) blue channel should be 1"); + } + } + + // ================================================================ + // + // BUG 7: ToNDArray(copy:true) 24bpp round-trip corrupts pixel data + // on narrow even-width images (2px wide) + // + // SEVERITY: High — silent data corruption. + // + // ROOT CAUSE: For 2px wide 24bpp: stride=8 (2*3=6, padded to 8). + // stride/width = 8/2 = 4. The copy path reshapes to (1, H, W, 4), + // treating the 2 padding bytes as a 4th channel. When this is + // round-tripped through ToBitmap, the pixel data boundaries are + // shifted by the phantom 4th channel, producing wrong colors. + // + // Example: SetPixel(1,0) = RGB(40,50,60). In memory (BGR): 60,50,40. + // With stride padding, row 0 = [30,20,10, 60,50,40, pad,pad]. + // Reshape as 4-channel: pixel(0,0)=[30,20,10,60], pixel(0,1)=[50,40,pad,pad]. + // Round-trip reads pixel(0,1) as RGB(pad,50,40) → wrong. + // + // ================================================================ + + /// + /// BUG 7: Round-trip 24bpp 2px wide — pixel values corrupted. + /// + [TestMethod] + public void Bug7_ToNDArray_CopyTrue_2pxWide24bpp_RoundTripCorruption() + { + var bmp = new Bitmap(2, 2, PixelFormat.Format24bppRgb); + bmp.SetPixel(0, 0, Color.FromArgb(10, 20, 30)); + bmp.SetPixel(1, 0, Color.FromArgb(40, 50, 60)); + bmp.SetPixel(0, 1, Color.FromArgb(70, 80, 90)); + bmp.SetPixel(1, 1, Color.FromArgb(100, 110, 120)); + + var nd = bmp.ToNDArray(copy: true, discardAlpha: false); + var bmp2 = nd.ToBitmap(); + + // Verify round-trip preserves pixel values + var orig_1_0 = bmp.GetPixel(1, 0); // (40, 50, 60) + var recovered_1_0 = bmp2.GetPixel(1, 0); + + recovered_1_0.R.Should().Be(orig_1_0.R, "R channel of pixel (1,0) should survive round-trip"); + recovered_1_0.G.Should().Be(orig_1_0.G, "G channel of pixel (1,0) should survive round-trip"); + recovered_1_0.B.Should().Be(orig_1_0.B, "B channel of pixel (1,0) should survive round-trip"); + } + + // ================================================================ + // + // BUG 8: extractFormatNumber() silently falls through for + // unsupported bbp values, leaving format as DontCare + // + // SEVERITY: Low — the Bitmap constructor catches this, but the + // error message is unhelpful ("Parameter is not valid" from GDI+). + // + // ROOT CAUSE: The switch in extractFormatNumber() (line 267-286) + // only handles bbp 3, 4, 6, 8. For any other value (1, 2, 5, etc.), + // format stays DontCare and the function returns bbp. Then + // `new Bitmap(width, height, format)` with DontCare throws a + // generic GDI+ error. The code should throw a descriptive + // ArgumentException before reaching the Bitmap constructor. + // + // ================================================================ + + /// + /// BUG 8: ToBitmap with 2-channel NDArray throws unhelpful GDI+ error. + /// extractFormatNumber() doesn't handle bbp=2, leaving format as DontCare. + /// + [TestMethod] + public void Bug8_ToBitmap_UnsupportedBpp_UnhelpfulError() + { + var nd = np.zeros(1, 2, 2, 2).astype(NPTypeCode.Byte); + + // Expected: descriptive ArgumentException about unsupported channel count + // Actual: "Parameter is not valid" from new Bitmap(w, h, DontCare) + Action act = () => nd.ToBitmap(); + act.Should().Throw() + .And.Message.Should().Contain("2", + "the error message should mention the unsupported byte-per-pixel " + + "count to help the user understand what went wrong, instead of " + + "a generic GDI+ 'Parameter is not valid' error."); + } + } +} diff --git a/test/NumSharp.UnitTest/OpenBugs.cs b/test/NumSharp.UnitTest/OpenBugs.cs new file mode 100644 index 000000000..e6892341b --- /dev/null +++ b/test/NumSharp.UnitTest/OpenBugs.cs @@ -0,0 +1,1944 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Linq; +using FluentAssertions; +using NumSharp; +using NumSharp.Backends; + +namespace NumSharp.UnitTest +{ + /// + /// Master collection of reproducible open bugs found during broadcast testing. + /// Each test asserts the CORRECT NumPy behavior as source of truth. + /// Tests FAIL while the bug exists, and PASS when the bug is fixed. + /// + /// All NumPy behaviors were verified by running actual Python code against + /// NumPy v2.4.2 on 2026-02-07. The Python verification script is documented + /// in the session transcript. + /// + /// When a bug is fixed, the test starts passing. Move passing tests to + /// the appropriate permanent test class (e.g., NpBroadcastFromNumPyTests). + /// + /// ===================================================================== + /// ARCHITECTURAL ROOT CAUSE ANALYSIS + /// ===================================================================== + /// + /// The majority of bugs trace back to a single architectural problem: + /// NumSharp has MULTIPLE code paths for traversing array elements, and + /// they don't all handle the ViewInfo + BroadcastInfo stride combination + /// correctly. + /// + /// Coordinate-based access (Shape.GetOffset(coords), used by GetInt32/ + /// GetDouble/etc.) is CORRECT — it properly combines the slice ViewInfo + /// strides with the broadcast BroadcastInfo zero-strides. But flat/linear + /// iteration paths (used by ToString, flatten, concatenate, np.minimum) + /// compute offsets differently and produce wrong results for broadcasted + /// arrays — especially when the source was sliced before broadcasting. + /// + /// This means: any code that accesses elements by coordinate will see the + /// right values, but any code that iterates linearly (for rendering, + /// copying, or element-wise operations) may see wrong values. + /// + /// Bug categories: + /// - Bugs 1, 11, 12: Linear iterator disagrees with indexed access + /// - Bug 5/9: np.minimum's specific iteration transposes inputs + /// - Bugs 6, 8: Comparison operators missing NDArray vs NDArray support + /// - Bug 7: np.allclose fundamentally broken (not broadcast-specific) + /// - Bugs 2, 3, 4: broadcast_to semantic/protection issues + /// - Bug 10: np.unique missing sort (not broadcast-specific) + /// - Bug 13: cumsum axis on broadcast reads garbage memory + /// - Bug 14: roll on broadcast produces zeros/wrong values + /// - Bug 15: sum/mean/var/std axis=0 on column-broadcast under-counts + /// - Bug 16: argsort crashes on any 2D array (not broadcast-specific) + /// - Bug 17: ROOT CAUSE — GetViewInternal clones broadcast data to + /// contiguous layout but attaches broadcast strides to the + /// clone (UnmanagedStorage.Slicing.cs:101). This stride/data + /// mismatch causes bugs 1, 11, 12, 13, 14, 15. + /// + /// Total: 17 distinct bugs, 38 test methods. + /// + [TestClass] + public class OpenBugs : TestClass + { + // ================================================================ + // + // BUG 1: ToString on broadcasted sliced arrays shows wrong values + // + // SEVERITY: High — affects all debugging/display of broadcast arrays. + // + // PATTERN: The most visible manifestation of the iterator-vs-indexed + // access divergence. GetInt32(i,j) uses Shape.GetOffset(coords) which + // correctly computes: baseOffset + (coord * viewStride) for each dim, + // where viewStride already accounts for the original slice strides. + // But ToString uses a linear NDIterator (or similar flat traversal) + // that computes offsets differently — it appears to use the broadcast + // strides directly against the raw storage pointer without going + // through the ViewInfo coordinate-to-offset translation. + // + // The result: GetInt32 returns correct values, but ToString shows + // wrong values. This affects reversed slices (negative strides), + // step slices (strides > 1), 2D sliced columns, and double-sliced + // (slice-of-slice) arrays when any of these are then broadcasted. + // + // EVIDENCE: Four variants tested below. In all cases, coordinate + // access (GetInt32) returns the correct NumPy values, but the string + // representation is wrong. The specific wrong values vary: + // - Reversed slice: shows forward order [0,1,2] instead of [2,1,0] + // - Step slice: shows scrambled [0,4,2] instead of [0,2,4] + // - Column slice: shows zeros [0,0,0] instead of [9,9,9] + // - Double slice: shows garbage [32,32,32,32] instead of [8,8,8,8] + // + // Note: Simple broadcasts WITHOUT slicing (e.g. broadcast_to([1,2,3], + // (2,3))) render correctly in ToString. The bug only manifests when + // the source array has non-trivial ViewInfo (was sliced before + // broadcasting). + // + // AFFECTED FILES: The rendering path in NDArray.ToString, likely + // using NDIterator or a raw pointer walk that doesn't account for + // the combined ViewInfo+BroadcastInfo stride calculation. + // + // PYTHON VERIFICATION: + // >>> np.broadcast_to(np.arange(3)[::-1], (2,3)) + // array([[2, 1, 0], [2, 1, 0]]) + // >>> np.broadcast_to(np.arange(6)[::2], (2,3)) + // array([[0, 2, 4], [0, 2, 4]]) + // >>> np.broadcast_to(np.arange(12).reshape(3,4)[:,1:2], (3,3)) + // array([[1, 1, 1], [5, 5, 5], [9, 9, 9]]) + // >>> x=np.arange(12).reshape(3,4); np.broadcast_to(x[::2,0:1], (2,4)) + // array([[0, 0, 0, 0], [8, 8, 8, 8]]) + // + // ================================================================ + + /// + /// BUG 1a: ToString ignores negative strides on broadcasted arrays. + /// + /// Setup: arange(3) = [0,1,2], reversed via [::-1] to [2,1,0], + /// then broadcast_to (2,3). The reversed slice has stride=-1 in + /// ViewInfo, and the broadcast adds a zero-stride row dimension. + /// + /// NumPy output: [[2, 1, 0], [2, 1, 0]] + /// NumSharp GetInt32: [2,1,0],[2,1,0] (CORRECT) + /// NumSharp ToString: [[0,1,2],[0,1,2]] (WRONG — forward order) + /// + /// The ToString iterator likely starts at the raw base pointer and + /// walks forward, ignoring that the ViewInfo stride is -1, which + /// means the logical element order is reversed from the physical + /// memory layout. + /// + [TestMethod] + public void Bug_ToString_ReversedSliceBroadcast() + { + var rev = np.arange(3)["::-1"]; // [2, 1, 0] + var brev = np.broadcast_to(rev, new Shape(2, 3)); + + // Coordinate access is correct + Assert.AreEqual(2, brev.GetInt32(0, 0)); + Assert.AreEqual(1, brev.GetInt32(0, 1)); + Assert.AreEqual(0, brev.GetInt32(0, 2)); + + // NumPy: ToString must show "2, 1, 0" (reversed order) + var str = brev.ToString(false); + str.Should().Contain("2, 1, 0", + "NumPy: broadcast_to(arange(3)[::-1], (2,3)) displays [[2,1,0],[2,1,0]]. " + + "NumSharp shows [[0,1,2],[0,1,2]] — the ToString iterator ignores the " + + "negative stride from the reversed slice when combined with broadcasting."); + } + + /// + /// BUG 1b: ToString shows scrambled values for step-sliced broadcast. + /// + /// Setup: arange(6) = [0,1,2,3,4,5], step-sliced via [::2] to + /// [0,2,4] (stride=2 in ViewInfo), then broadcast_to (2,3). + /// + /// NumPy output: [[0, 2, 4], [0, 2, 4]] + /// NumSharp GetInt32: [0,2,4],[0,2,4] (CORRECT) + /// NumSharp ToString: [[0,4,2],[0,4,2]] (WRONG — scrambled) + /// + /// The values 0, 4, 2 suggest the iterator is using stride=2 but + /// wrapping around or mis-computing offsets: offset 0→value 0, + /// offset 2→value 2 (skipped), offset 4→value 4, then offset + /// 1→value 1 (but shows 4?). The exact scrambling pattern suggests + /// the broadcast stride and the view stride are being multiplied + /// or combined incorrectly. + /// + [TestMethod] + public void Bug_ToString_StepSliceBroadcast() + { + var stepped = np.arange(6)["::2"]; // [0, 2, 4] + var bstepped = np.broadcast_to(stepped, new Shape(2, 3)); + + // Coordinate access is correct + Assert.AreEqual(0, bstepped.GetInt32(0, 0)); + Assert.AreEqual(2, bstepped.GetInt32(0, 1)); + Assert.AreEqual(4, bstepped.GetInt32(0, 2)); + + // NumPy: ToString must show "0, 2, 4" (in order) + var str = bstepped.ToString(false); + str.Should().Contain("0, 2, 4", + "NumPy: broadcast_to(arange(6)[::2], (2,3)) displays [[0,2,4],[0,2,4]]. " + + "NumSharp shows [[0,4,2],[0,4,2]] — the ToString iterator mis-computes " + + "offsets when the source has stride=2 from step-slicing."); + } + + /// + /// BUG 1c: ToString shows zeros in last row for 2D sliced column broadcast. + /// + /// Setup: arange(12).reshape(3,4) sliced to column 1 via [:,1:2] + /// giving (3,1) array [[1],[5],[9]], then broadcast_to (3,3). + /// The column slice has a row stride of 4 (stepping over the full + /// 4-wide row to get to the next row's column 1). + /// + /// NumPy output: [[1,1,1], [5,5,5], [9,9,9]] + /// NumSharp GetInt32: [[1,1,1],[5,5,5],[9,9,9]] (CORRECT) + /// NumSharp ToString: [[1,1,1],[5,5,5],[0,0,0]] (WRONG — last row zeros) + /// + /// The zeros in the last row suggest the iterator overflows past + /// the allocated storage. The 3x4 array has 12 ints = 48 bytes. + /// Column 1 values are at byte offsets 4, 20, 36. The iterator + /// appears to walk linearly with some stride that misses offset 36 + /// and lands in zeroed/garbage memory beyond the allocation. + /// + [TestMethod] + public void Bug_ToString_SlicedColumnBroadcast() + { + var x = np.arange(12).reshape(3, 4); + var col = x[":, 1:2"]; // (3,1): [[1],[5],[9]] + var bcol = np.broadcast_to(col, new Shape(3, 3)); + + // Coordinate access is correct + Assert.AreEqual(9, bcol.GetInt32(2, 0)); + Assert.AreEqual(9, bcol.GetInt32(2, 2)); + + // NumPy: ToString must show "9, 9, 9" in last row + var str = bcol.ToString(false); + str.Should().Contain("9, 9, 9", + "NumPy: broadcast_to(x[:,1:2], (3,3)) displays [[1,1,1],[5,5,5],[9,9,9]]. " + + "NumSharp shows [[1,1,1],[5,5,5],[0,0,0]] — the ToString iterator " + + "overflows past the storage for the last row of the sliced+broadcast array."); + } + + /// + /// BUG 1d: ToString for double-sliced broadcast shows garbage. + /// + /// Setup: arange(12).reshape(3,4) → x[::2,:] gives rows 0,2 → + /// (2,4) view with row stride=8 (skipping row 1). Then [:,0:1] + /// gives column 0 → (2,1) with values [[0],[8]]. Then broadcast + /// to (2,4). + /// + /// This is the most complex case: a slice-of-a-slice (double + /// ViewInfo nesting) combined with broadcasting. + /// + /// NumPy output: [[0,0,0,0], [8,8,8,8]] + /// NumSharp GetInt32: [[0,0,0,0],[8,8,8,8]] (CORRECT) + /// NumSharp ToString: [[0,0,0,0],[32,32,32,32]] (WRONG — garbage) + /// + /// The value 32 (0x20) in the second row indicates the iterator + /// is reading from a completely wrong memory offset. Since the + /// source array contains values 0-11 (each 4 bytes), value 32 + /// cannot come from any valid element — it's reading from beyond + /// the array or from uninitialized memory. The double-slicing + /// creates a compound stride that the ToString iterator fails + /// to resolve correctly. + /// + [TestMethod] + public void Bug_ToString_DoubleSlicedBroadcast() + { + var x = np.arange(12).reshape(3, 4); + var dslice = x["::2, :"]; + var dslice_col = dslice[":, 0:1"]; + var bdslice = np.broadcast_to(dslice_col, new Shape(2, 4)); + + // Coordinate access is correct + Assert.AreEqual(8, bdslice.GetInt32(1, 0)); + + // NumPy: ToString must show "8, 8, 8, 8" in second row + var str = bdslice.ToString(false); + str.Should().Contain("8, 8, 8, 8", + "NumPy: broadcast_to(x[::2,0:1], (2,4)) displays [[0,0,0,0],[8,8,8,8]]. " + + "NumSharp shows garbage values (e.g. 32) in the second row — the double-" + + "sliced ViewInfo compound stride is not resolved by the ToString iterator."); + } + + // ================================================================ + // + // BUG 2: broadcast_to allows write-through (no read-only flag) + // + // SEVERITY: Medium — silent data corruption risk. + // + // In NumPy, broadcast_to returns a read-only view: + // y = np.broadcast_to(x, (2,4)) + // y.flags.writeable → False + // y[0,0] = 999 → ValueError: assignment destination is read-only + // + // This is critical because broadcast views have zero-stride dimensions. + // Multiple logical positions (e.g. y[0,0] and y[1,0]) map to the SAME + // physical memory location. Writing to y[0,0] would silently modify + // y[1,0] and the original array x[0], violating the broadcasting + // contract that each row is an independent "copy" of the source. + // + // NumSharp has no read-only concept on NDArray/UnmanagedStorage. + // SetInt32(999, 0, 0) succeeds silently, corrupting the original. + // + // FIX APPROACH: Add a Writeable flag to NDArray or UnmanagedStorage. + // broadcast_to should set Writeable=false. All Set* methods should + // check this flag and throw InvalidOperationException. + // + // PYTHON VERIFICATION: + // >>> x = np.array([1,2,3,4]) + // >>> y = np.broadcast_to(x, (2,4)) + // >>> y.flags.writeable + // False + // >>> y[0,0] = 999 + // ValueError: assignment destination is read-only + // + // ================================================================ + + /// + /// BUG 2: broadcast_to result is not read-only. Writing to the + /// broadcast view succeeds and modifies the original source array. + /// + /// NumPy: y[0,0] = 999 raises ValueError. + /// NumSharp: SetInt32(999, 0, 0) succeeds silently. + /// + [TestMethod] + public void Bug_BroadcastTo_NoReadOnlyProtection() + { + var x = np.array(new int[] { 1, 2, 3, 4 }); + var bx = np.broadcast_to(x, new Shape(2, 4)); + + // NumPy: Writing to broadcast result must throw + Action write = () => bx.SetInt32(999, 0, 0); + write.Should().Throw( + "NumPy raises ValueError: assignment destination is read-only. " + + "NumSharp has no read-only flag, so SetInt32 succeeds and silently " + + "modifies the original array x[0] to 999, corrupting shared memory."); + } + + // ================================================================ + // + // BUG 3: broadcast_to uses bilateral instead of one-directional + // + // SEVERITY: Medium — wrong API semantics, accepts invalid inputs. + // + // NumPy's broadcast_to(array, shape) is STRICTLY one-directional: + // it only stretches dimensions of the source array that are size 1 + // to match the target shape. It NEVER modifies the target shape. + // + // Examples that NumPy rejects: + // broadcast_to(ones(3), (1,)) → ValueError (can't shrink 3→1) + // broadcast_to(ones(1,2), (2,1)) → ValueError (can't reshape 2→1) + // + // NumSharp's implementation delegates to DefaultEngine.Broadcast(from, + // against) which performs MUTUAL/bilateral broadcasting. This means: + // broadcast_to(ones(3), (1,)) → returns shape (3,) [target stretched] + // broadcast_to(ones(1,2), (2,1)) → returns shape (2,2) [both stretched] + // + // This breaks user code that relies on broadcast_to to validate + // shape compatibility in one direction only. + // + // FIX APPROACH: np.broadcast_to.cs should validate that every + // dimension of the source either equals the target or is 1. If + // any target dimension is smaller than the source, throw. + // + // PYTHON VERIFICATION: + // >>> np.broadcast_to(np.ones(3), (1,)) + // ValueError: operands could not be broadcast together with + // remapped shapes [original->remapped]: (3,) and requested shape (1,) + // >>> np.broadcast_to(np.ones((1,2)), (2,1)) + // ValueError: operands could not be broadcast together with + // remapped shapes [original->remapped]: (1,2) and requested shape (2,1) + // + // ================================================================ + + /// + /// BUG 3: broadcast_to uses bilateral broadcasting instead of + /// NumPy's one-directional semantics. Cases that should throw + /// instead succeed by stretching the target shape. + /// + /// NumPy: broadcast_to(ones(3), (1,)) raises ValueError. + /// NumSharp: Returns shape (3,) — stretched the target. + /// + /// NumPy: broadcast_to(ones(1,2), (2,1)) raises ValueError. + /// NumSharp: Returns shape (2,2) — stretched both. + /// + [TestMethod] + public void Bug_BroadcastTo_BilateralSemantics() + { + // NumPy: broadcast_to(ones(3), (1,)) must throw + new Action(() => np.broadcast_to(np.ones(new Shape(3)), new Shape(1))) + .Should().Throw( + "NumPy raises ValueError for broadcast_to((3,), (1,)). " + + "NumSharp's bilateral broadcast succeeds, returning shape (3,) " + + "by stretching the target shape (1,) up to match the source."); + + // NumPy: broadcast_to(ones(1,2), (2,1)) must throw + new Action(() => np.broadcast_to(np.ones(new Shape(1, 2)), new Shape(2, 1))) + .Should().Throw( + "NumPy raises ValueError for broadcast_to((1,2), (2,1)). " + + "NumSharp's bilateral broadcast succeeds, returning shape (2,2) " + + "by stretching both source dim 0 (1→2) and target dim 1 (1→2)."); + } + + // ================================================================ + // + // BUG 4: Re-broadcast inconsistency (IsBroadcasted guard) + // + // SEVERITY: Medium — blocks legitimate chained broadcasting. + // + // NumPy handles re-broadcasting transparently: + // bx, _ = broadcast_arrays(ones(1,3), ones(3,1)) # bx is (3,3) + // broadcast_to(bx, (2,3,3)) # works fine → (2,3,3) + // + // NumSharp's 2-arg Broadcast(Shape, Shape) at line ~300 of + // Default.Broadcasting.cs has an explicit guard: + // if (leftShape.IsBroadcasted || rightShape.IsBroadcasted) + // throw new NotSupportedException(); + // + // This blocks ALL re-broadcasting through the 2-arg path. But the + // N-array overload Broadcast(Shape[]) does NOT have this guard, + // creating an inconsistency: + // broadcast_to(bx, (2,3,3)) → throws (2-arg path) + // broadcast_arrays(bx, ones(2,3,3)) → works (N-arg path) + // + // The guard was likely added as a safety measure to avoid complex + // stride-on-stride calculations, but it's overly conservative. + // + // FIX APPROACH: Remove the IsBroadcasted guard or make the 2-arg + // Broadcast correctly handle already-broadcasted shapes by resolving + // the effective strides before re-broadcasting. + // + // PYTHON VERIFICATION: + // >>> x, _ = np.broadcast_arrays(np.ones((1,3)), np.ones((3,1))) + // >>> np.broadcast_to(x, (2,3,3)).shape + // (2, 3, 3) + // + // ================================================================ + + /// + /// BUG 4: Re-broadcasting an already-broadcasted array to a larger + /// shape throws NotSupportedException. NumPy handles this transparently. + /// + /// Setup: broadcast_arrays produces (3,3) broadcasted result. + /// Then broadcast_to that (3,3) to (2,3,3) should just add a + /// new leading dimension with stride=0, but the IsBroadcasted + /// guard blocks it. + /// + [TestMethod] + public void Bug_ReBroadcast_Inconsistency() + { + var x = np.ones(new Shape(1, 3)); + var y = np.ones(new Shape(3, 1)); + var (bx, _) = np.broadcast_arrays(x, y); // bx is (3,3), IsBroadcasted=true + + // NumPy: Re-broadcasting must work transparently + NDArray result = null; + new Action(() => result = np.broadcast_to(bx, new Shape(2, 3, 3))) + .Should().NotThrow( + "NumPy handles re-broadcasting transparently: broadcast_to((3,3)→(2,3,3)) " + + "just adds a leading dimension with stride=0. NumSharp's 2-arg " + + "Broadcast(Shape,Shape) throws NotSupportedException due to an explicit " + + "guard: if (leftShape.IsBroadcasted || rightShape.IsBroadcasted) throw. " + + "The N-array overload Broadcast(Shape[]) lacks this guard, creating " + + "an inconsistency where broadcast_arrays succeeds but broadcast_to throws."); + + result.Should().NotBeNull(); + result.shape.Should().BeEquivalentTo(new[] { 2, 3, 3 }); + } + + // ================================================================ + // + // BUG 5 / BUG 9: np.minimum broadcast produces wrong values + // + // SEVERITY: High — silently returns wrong computation results. + // + // np.minimum with broadcasting produces transposed/swapped values. + // The bug affects ALL numeric types: int, float, double, long, byte. + // + // NumPy: minimum([1,5,3], [[2],[4]]) = [[1,2,2],[1,4,3]] + // NumSharp: Returns [[1,4,2],[1,2,3]] for ALL types. + // + // The pattern: row 0 should use b[0]=2 for comparisons, but gets + // b[1]=4. Row 1 should use b[1]=4, but gets b[0]=2. The b values + // are transposed between rows. + // + // CRITICAL OBSERVATION: np.maximum with the EXACT same inputs + // returns CORRECT values: [[2,5,3],[4,5,4]]. This proves the + // broadcasting infrastructure (shape resolution, stride setup) is + // correct — the bug is specifically in minimum's iteration logic. + // + // Most likely: minimum creates its MultiIterator or paired iteration + // with the two input arrays in swapped order compared to maximum. + // Since minimum(a,b) ≠ minimum(b,a) when both are < and > the + // comparison value, swapping the iteration order of the two broadcast + // dimensions produces the transposed result. + // + // FIX APPROACH: Check the argument order in np.minimum's call to + // the broadcast iteration. Compare with np.maximum which works. + // + // PYTHON VERIFICATION: + // >>> np.minimum(np.array([1,5,3]), np.array([[2],[4]])) + // array([[1, 2, 2], [1, 4, 3]]) + // >>> np.minimum(np.array([1.,5.,3.]), np.array([[2.],[4.]])) + // array([[1., 2., 2.], [1., 4., 3.]]) + // >>> np.minimum(np.array([1,5,3], dtype=np.float32), + // ... np.array([[2],[4]], dtype=np.float32)) + // array([[1., 2., 2.], [1., 4., 3.]], dtype=float32) + // + // ================================================================ + + /// + /// BUG 5: np.minimum with int32 broadcast returns wrong values. + /// Row 0 gets b[1]=4 instead of b[0]=2, and vice versa. + /// + /// NumPy: [[1, 2, 2], [1, 4, 3]] + /// NumSharp: [[1, 4, 2], [1, 2, 3]] (b values transposed) + /// + [TestMethod] + public void Bug_Minimum_IntBroadcast_WrongValues() + { + var a = np.array(new int[] { 1, 5, 3 }); + var b = np.array(new int[,] { { 2 }, { 4 } }); + var r = np.minimum(a, b); + + r.shape.Should().BeEquivalentTo(new[] { 2, 3 }); + + var expected = np.array(new int[,] { { 1, 2, 2 }, { 1, 4, 3 } }); + np.array_equal(r, expected).Should().BeTrue( + "NumPy: minimum([1,5,3], [[2],[4]]) = [[1,2,2],[1,4,3]]. " + + "NumSharp returns [[1,4,2],[1,2,3]] — the b column vector values " + + "are transposed between rows, suggesting the broadcast iteration " + + "in minimum reads the two inputs in swapped order. np.maximum " + + "with identical inputs returns correct values."); + } + + /// + /// BUG 9a: np.minimum with double broadcast — same transposition bug. + /// Confirms the bug is type-independent (not an int-specific code path). + /// + /// NumPy: [[1., 2., 2.], [1., 4., 3.]] + /// NumSharp: [[1., 4., 2.], [1., 2., 3.]] + /// + [TestMethod] + public void Bug_Minimum_DoubleBroadcast_WrongValues() + { + var a = np.array(new double[] { 1, 5, 3 }); + var b = np.array(new double[,] { { 2 }, { 4 } }); + var r = np.minimum(a, b); + + r.shape.Should().BeEquivalentTo(new[] { 2, 3 }); + + var expected = np.array(new double[,] { { 1, 2, 2 }, { 1, 4, 3 } }); + np.array_equal(r, expected).Should().BeTrue( + "NumPy: minimum([1.,5.,3.], [[2.],[4.]]) = [[1,2,2],[1,4,3]]. " + + "NumSharp returns [[1,4,2],[1,2,3]] for double — same transposition " + + "as int, confirming the bug is in the iteration logic, not type-specific."); + } + + /// + /// BUG 9b: np.minimum with float32 broadcast — same transposition bug. + /// + /// NumPy: [[1f, 2f, 2f], [1f, 4f, 3f]] + /// NumSharp: [[1f, 4f, 2f], [1f, 2f, 3f]] + /// + [TestMethod] + public void Bug_Minimum_FloatBroadcast_WrongValues() + { + var a = np.array(new float[] { 1f, 5f, 3f }); + var b = np.array(new float[,] { { 2f }, { 4f } }); + var r = np.minimum(a, b); + + var expected = np.array(new float[,] { { 1f, 2f, 2f }, { 1f, 4f, 3f } }); + np.array_equal(r, expected).Should().BeTrue( + "NumPy: minimum([1,5,3], [[2],[4]]) = [[1,2,2],[1,4,3]] for float32. " + + "NumSharp returns [[1,4,2],[1,2,3]] — identical transposition to int/double."); + } + + // ================================================================ + // + // BUG 6: != operator throws InvalidCastException (NDArray vs NDArray) + // + // SEVERITY: High — element-wise != between arrays is non-functional. + // + // The != operator (NDArray.NotEquals.cs) has a different bug from + // the other comparison operators. While >, <, >=, <= throw + // IncorrectShapeException (they don't support NDArray vs NDArray at + // all — see Bug 8), != attempts to handle it but goes through the + // wrong code path. + // + // The operator signature is: op_Inequality(NDArray np, Object obj) + // When the right operand is an NDArray, it gets boxed as Object. + // The implementation then tries to convert it via IConvertible + // (likely to treat it as a scalar), which fails because NDArray + // doesn't implement IConvertible. + // + // The == operator works correctly for NDArray vs NDArray because + // it has a separate overload that handles this case with broadcasting. + // + // FIX APPROACH: Add an NDArray-typed overload for !=, similar to ==. + // Or fix the Object overload to detect NDArray and dispatch to + // element-wise comparison with broadcasting. + // + // PYTHON VERIFICATION: + // >>> np.array([1,2,3]) != np.array([[1],[2],[3]]) + // array([[False, True, True], + // [ True, False, True], + // [ True, True, False]]) + // + // ================================================================ + + /// + /// BUG 6: The != operator throws InvalidCastException when both + /// operands are NDArrays (even same shape). The implementation + /// tries to cast the NDArray RHS to IConvertible (scalar path) + /// instead of performing element-wise comparison. + /// + /// NumPy: Returns (3,3) bool with False on diagonal. + /// NumSharp: InvalidCastException: Unable to cast 'NDArray' to 'IConvertible'. + /// + [TestMethod] + public void Bug_NotEquals_NDArrayBroadcast_Throws() + { + var a = np.array(new int[] { 1, 2, 3 }); + var b = np.array(new int[,] { { 1 }, { 2 }, { 3 } }); + + NDArray result = null; + new Action(() => { result = (a != b); }) + .Should().NotThrow( + "NumPy: array([1,2,3]) != array([[1],[2],[3]]) returns a (3,3) bool array. " + + "NumSharp throws InvalidCastException because op_Inequality(NDArray, Object) " + + "tries to convert the NDArray RHS via IConvertible instead of dispatching " + + "to element-wise broadcast comparison."); + + result.Should().NotBeNull(); + result.shape.Should().BeEquivalentTo(new[] { 3, 3 }); + + // Diagonal = False (equal), off-diagonal = True (not equal) + result.GetBoolean(0, 0).Should().BeFalse("1 == 1"); + result.GetBoolean(1, 1).Should().BeFalse("2 == 2"); + result.GetBoolean(2, 2).Should().BeFalse("3 == 3"); + result.GetBoolean(0, 1).Should().BeTrue("1 != 2"); + result.GetBoolean(1, 0).Should().BeTrue("2 != 1"); + } + + // ================================================================ + // + // BUG 7: np.allclose ALWAYS throws NullReferenceException + // + // SEVERITY: Critical — the function is entirely non-functional. + // + // np.allclose throws NullReferenceException for ALL inputs, + // including allclose(a, a) with a simple 1D array. This is NOT + // a broadcast-specific bug — the function is fundamentally broken. + // + // The crash trace: + // np.allclose(a, b) + // → DefaultEngine.AllClose(a, b, rtol, atol, equal_nan) + // at Default.AllClose.cs:line 23 + // → np.all(NDArray a) + // at np.all.cs:line 29 ← NullReferenceException + // + // The AllClose implementation likely computes abs(a-b) <= atol+rtol*abs(b) + // and then calls np.all() on the boolean result. But somewhere in + // that chain, a null NDArray is produced (possibly from the <= operator + // failing to handle NDArray vs NDArray — see Bug 8) and passed to + // np.all(), which dereferences it and throws NullReferenceException. + // + // FIX APPROACH: Debug Default.AllClose.cs line 23. The intermediate + // boolean array from the element-wise comparison is likely null. + // Fixing Bug 8 (comparison operators) may cascade-fix this. + // + // PYTHON VERIFICATION: + // >>> np.allclose(np.array([1.,2.,3.]), np.array([1.,2.,3.])) + // True + // >>> np.allclose(np.array([1.,2.,3.]), + // ... np.array([[1.,2.,3.],[1.,2.,3.]])) + // True + // + // ================================================================ + + /// + /// BUG 7a: np.allclose crashes even for the simplest case: + /// comparing an array to itself. + /// + /// NumPy: allclose(a, a) returns True. + /// NumSharp: NullReferenceException at np.all.cs:line 29. + /// + [TestMethod] + public void Bug_Allclose_AlwaysThrows() + { + var a = np.array(new double[] { 1.0, 2.0, 3.0 }); + + bool result = false; + new Action(() => result = np.allclose(a, a)) + .Should().NotThrow( + "NumPy: allclose(a, a) returns True — the simplest possible case. " + + "NumSharp throws NullReferenceException because the intermediate boolean " + + "array (from abs(a-a) <= atol comparison) is null when passed to np.all(). " + + "This likely cascades from Bug 8 (<= operator not supporting NDArray vs NDArray)."); + + result.Should().BeTrue(); + } + + /// + /// BUG 7b: np.allclose also crashes with broadcast-compatible shapes. + /// (Consequence of 7a — documenting separately for completeness.) + /// + /// NumPy: allclose(shape(3,), shape(2,3)) returns True when all elements match. + /// NumSharp: NullReferenceException. + /// + [TestMethod] + public void Bug_Allclose_BroadcastThrows() + { + var a = np.array(new double[] { 1.0, 2.0, 3.0 }); // shape (3,) + var b = np.array(new double[,] { { 1, 2, 3 }, { 1, 2, 3 } }); // shape (2,3) + + bool result = false; + new Action(() => result = np.allclose(a, b)) + .Should().NotThrow( + "NumPy: allclose(array([1,2,3]), array([[1,2,3],[1,2,3]])) returns True. " + + "NumSharp throws NullReferenceException — same root cause as Bug 7a."); + + result.Should().BeTrue(); + } + + // ================================================================ + // + // BUG 8: >, <, >=, <= throw IncorrectShapeException + // for NDArray vs NDArray (even same shape) + // + // SEVERITY: Critical — element-wise comparison between arrays is + // entirely missing for 4 of 6 comparison operators. + // + // The comparison operators >, <, >=, <= only support NDArray vs + // scalar (e.g., array > 5). When both operands are NDArray — even + // with the EXACT SAME shape — they throw IncorrectShapeException: + // "This method does not work with this shape or was not already + // implemented." + // + // This contrasts with == which correctly supports NDArray vs NDArray + // with broadcasting. The != operator attempts it but crashes via + // a different path (see Bug 6). + // + // Operator support matrix: + // == NDArray vs NDArray: WORKS (with broadcasting) + // != NDArray vs NDArray: CRASHES (InvalidCastException) — Bug 6 + // > NDArray vs NDArray: CRASHES (IncorrectShapeException) — Bug 8 + // < NDArray vs NDArray: CRASHES (IncorrectShapeException) — Bug 8 + // >= NDArray vs NDArray: CRASHES (IncorrectShapeException) — Bug 8 + // <= NDArray vs NDArray: CRASHES (IncorrectShapeException) — Bug 8 + // All NDArray vs scalar: WORKS + // + // This also cascade-causes Bug 7 (np.allclose) because allclose + // internally needs to evaluate abs(a-b) <= tolerance, which requires + // NDArray <= NDArray to work. + // + // FIX APPROACH: Implement NDArray vs NDArray comparison in + // NDArray.Greater.cs and NDArray.Lower.cs, following the pattern + // used by NDArray.Equals.cs (which works). Should support both + // same-shape and broadcast-compatible shapes. + // + // PYTHON VERIFICATION: + // >>> np.array([1,5,3]) > np.array([2,4,3]) + // array([False, True, False]) + // >>> np.array([1,5,3]) > np.array([[2],[4]]) + // array([[False, True, True], [False, True, False]]) + // + // ================================================================ + + /// + /// BUG 8a: > operator throws for NDArray vs NDArray, same shape. + /// Even the simplest case (two 1D arrays of equal length) fails. + /// + /// NumPy: array([1,5,3]) > array([2,4,3]) = [False,True,False] + /// NumSharp: IncorrectShapeException + /// + [TestMethod] + public void Bug_GreaterThan_NDArrayVsNDArray_SameShape() + { + var a = np.array(new int[] { 1, 5, 3 }); + var b = np.array(new int[] { 2, 4, 3 }); + + NDArray result = null; + new Action(() => { result = (a > b); }) + .Should().NotThrow( + "NumPy: array([1,5,3]) > array([2,4,3]) returns [False,True,False]. " + + "NumSharp throws IncorrectShapeException because the > operator only " + + "supports NDArray vs scalar, not NDArray vs NDArray."); + + result.Should().NotBeNull(); + result.GetBoolean(0).Should().BeFalse("1 > 2 is False"); + result.GetBoolean(1).Should().BeTrue("5 > 4 is True"); + result.GetBoolean(2).Should().BeFalse("3 > 3 is False"); + } + + /// + /// BUG 8b: < operator throws for NDArray vs NDArray, same shape. + /// + /// NumPy: array([1,5,3]) < array([2,4,3]) = [True,False,False] + /// NumSharp: IncorrectShapeException + /// + [TestMethod] + public void Bug_LessThan_NDArrayVsNDArray_SameShape() + { + var a = np.array(new int[] { 1, 5, 3 }); + var b = np.array(new int[] { 2, 4, 3 }); + + NDArray result = null; + new Action(() => { result = (a < b); }) + .Should().NotThrow( + "NumPy: array([1,5,3]) < array([2,4,3]) returns [True,False,False]. " + + "NumSharp throws IncorrectShapeException — same missing implementation as >."); + + result.Should().NotBeNull(); + result.GetBoolean(0).Should().BeTrue("1 < 2 is True"); + result.GetBoolean(1).Should().BeFalse("5 < 4 is False"); + result.GetBoolean(2).Should().BeFalse("3 < 3 is False"); + } + + /// + /// BUG 8c: > operator throws for NDArray vs NDArray with broadcast. + /// Not only is same-shape broken, but broadcast-compatible shapes + /// also fail. + /// + /// NumPy: array([1,5,3]) > array([[2],[4]]) = [[F,T,T],[F,T,F]] + /// NumSharp: IncorrectShapeException + /// + [TestMethod] + public void Bug_GreaterThan_NDArrayVsNDArray_Broadcast() + { + var a = np.array(new int[] { 1, 5, 3 }); // (3,) + var b = np.array(new int[,] { { 2 }, { 4 } }); // (2,1) + + NDArray result = null; + new Action(() => { result = (a > b); }) + .Should().NotThrow( + "NumPy: array([1,5,3]) > array([[2],[4]]) returns (2,3) bool " + + "[[F,T,T],[F,T,F]]. NumSharp throws IncorrectShapeException."); + + result.Should().NotBeNull(); + result.shape.Should().BeEquivalentTo(new[] { 2, 3 }); + result.GetBoolean(0, 0).Should().BeFalse("1 > 2 is False"); + result.GetBoolean(0, 1).Should().BeTrue("5 > 2 is True"); + result.GetBoolean(0, 2).Should().BeTrue("3 > 2 is True"); + result.GetBoolean(1, 0).Should().BeFalse("1 > 4 is False"); + result.GetBoolean(1, 1).Should().BeTrue("5 > 4 is True"); + result.GetBoolean(1, 2).Should().BeFalse("3 > 4 is False"); + } + + // ================================================================ + // + // BUG 10: np.unique returns unsorted results + // + // SEVERITY: Medium — violates NumPy's sorted-output guarantee. + // + // NumPy's np.unique() guarantees sorted output: + // "Returns the sorted unique elements of an array." + // — numpy.org/doc/stable/reference/generated/numpy.unique.html + // + // NumSharp's np.unique returns elements in first-encounter order, + // not sorted. This is NOT a broadcast bug — it affects all arrays. + // + // NumPy: unique([3,1,2,1,3]) = [1,2,3] (sorted) + // NumSharp: unique([3,1,2,1,3]) = [3,1,2] (encounter order) + // + // Root cause: The implementation likely uses a HashSet or LinkedHashSet + // to collect unique values, which preserves insertion order but does + // not sort. NumPy internally sorts the array first, then removes + // consecutive duplicates. + // + // FIX APPROACH: Sort the unique values before returning. Either sort + // the result array, or use a SortedSet instead of HashSet. + // + // PYTHON VERIFICATION: + // >>> np.unique(np.array([3, 1, 2, 1, 3])) + // array([1, 2, 3]) + // + // ================================================================ + + /// + /// BUG 10: np.unique returns unsorted results. + /// + /// NumPy: unique([3,1,2,1,3]) = [1,2,3] (always sorted). + /// NumSharp: Returns [3,1,2] (encounter/insertion order). + /// + [TestMethod] + public void Bug_Unique_NotSorted() + { + var a = np.array(new int[] { 3, 1, 2, 1, 3 }); + var u = np.unique(a); + + u.size.Should().Be(3, "There are 3 unique values"); + + // NumPy guarantees sorted output + u.GetInt32(0).Should().Be(1, + "NumPy: unique returns sorted unique values. First element must be 1. " + + "NumSharp returns 3 (first-encountered value)."); + u.GetInt32(1).Should().Be(2, + "NumPy: Second unique element must be 2. NumSharp returns 1."); + u.GetInt32(2).Should().Be(3, + "NumPy: Third unique element must be 3. NumSharp returns 2."); + } + + // ================================================================ + // + // BUG 11: flatten() on column-broadcast gives wrong order + // + // SEVERITY: High — silently returns wrong element ordering. + // + // flatten() should always return elements in C-order (row-major): + // iterate the last dimension fastest. For a (3,3) array: + // [0,0] [0,1] [0,2] [1,0] [1,1] [1,2] [2,0] [2,1] [2,2] + // + // For broadcast_to([[1],[2],[3]], (3,3)): + // C-order: [1,1,1, 2,2,2, 3,3,3] (each row repeated) + // + // NumSharp returns: [1,2,3, 1,2,3, 1,2,3] (column-major order) + // + // This is the flatten/ravel manifestation of Bug 1's iterator + // problem. The flatten implementation uses a linear traversal that + // doesn't correctly handle zero-stride broadcast dimensions. + // + // When the COLUMN dimension has stride=0 (row-vector broadcast, + // e.g. broadcast_to([1,2,3], (3,3))), flatten happens to produce + // the correct result: [1,2,3,1,2,3,1,2,3] — because the linear + // traversal happens to match C-order for that memory layout. + // + // But when the ROW dimension has stride=0 (column-vector broadcast, + // e.g. broadcast_to([[1],[2],[3]], (3,3))), the linear traversal + // iterates down columns first (Fortran-order) instead of across + // rows first (C-order). + // + // Interestingly, np.ravel() on the same array returns CORRECT + // results [1,1,1,2,2,2,3,3,3]. This suggests ravel uses a + // different code path than flatten for broadcasted arrays. + // + // PYTHON VERIFICATION: + // >>> np.broadcast_to(np.array([[1],[2],[3]]), (3,3)).flatten() + // array([1, 1, 1, 2, 2, 2, 3, 3, 3]) + // + // ================================================================ + + /// + /// BUG 11: flatten() on column-broadcast array iterates in + /// Fortran-order instead of C-order. + /// + /// NumPy: broadcast_to([[1],[2],[3]], (3,3)).flatten() = [1,1,1,2,2,2,3,3,3] + /// NumSharp: Returns [1,2,3,1,2,3,1,2,3] (column-major iteration). + /// + /// Note: ravel() returns correct results for the same input, + /// suggesting it uses a different iteration path than flatten(). + /// + [TestMethod] + public void Bug_Flatten_ColumnBroadcast_WrongOrder() + { + var a = np.array(new int[,] { { 1 }, { 2 }, { 3 } }); // (3,1) + var b = np.broadcast_to(a, new Shape(3, 3)); + + var flat = b.flatten(); + flat.size.Should().Be(9); + + // NumPy C-order: row-major → [1,1,1,2,2,2,3,3,3] + var expected = np.array(new int[] { 1, 1, 1, 2, 2, 2, 3, 3, 3 }); + np.array_equal(flat, expected).Should().BeTrue( + "NumPy: broadcast_to([[1],[2],[3]], (3,3)).flatten() = [1,1,1,2,2,2,3,3,3] " + + "(C-order / row-major). NumSharp returns [1,2,3,1,2,3,1,2,3] — iterating " + + "down columns first (Fortran-order) because the flatten iterator doesn't " + + "account for the zero-stride row dimension in the column-broadcast layout. " + + "ravel() on the same array returns correct results via a different code path."); + } + + // ================================================================ + // + // BUG 12: concatenate/hstack/vstack on broadcasted arrays + // produces wrong values + // + // SEVERITY: High — silently returns wrong array contents. + // + // np.concatenate (and hstack/vstack which delegate to it) produces + // wrong values when any input array is broadcasted. The shape of + // the result is correct, but the element values are wrong. + // + // The bug is in the COPY step: when concatenate copies elements + // from a broadcasted source array into the destination, it uses + // a linear offset calculation that doesn't account for zero-stride + // broadcast dimensions. The result is that rows get duplicated + // (column-broadcast), shifted (sliced+broadcast), or contain + // garbage values (reading past allocation). + // + // WORKAROUND: Call np.copy() on broadcasted arrays before passing + // them to concatenate/hstack/vstack. This materializes the broadcast + // into contiguous memory, which concatenate can then handle correctly. + // + // Verified: hstack(copy(a), copy(b)) returns correct results. + // + // Three variants tested: + // a) hstack with column-broadcast inputs → second row copies first + // b) vstack with sliced+broadcast input → rows shifted + // c) concatenate axis=0 with sliced+broadcast → garbage/zeros + // + // PYTHON VERIFICATION: + // >>> a = np.broadcast_to(np.array([[1],[2]]), (2,2)) + // >>> b = np.broadcast_to(np.array([[3],[4]]), (2,3)) + // >>> np.hstack([a, b]) + // array([[1, 1, 3, 3, 3], [2, 2, 4, 4, 4]]) + // >>> x = np.arange(6).reshape(3,2) + // >>> np.vstack([np.broadcast_to(x[:,0:1],(3,3)), [[10,20,30]]]) + // array([[ 0, 0, 0], [ 2, 2, 2], [ 4, 4, 4], [10, 20, 30]]) + // >>> x = np.arange(12).reshape(3,4) + // >>> np.concatenate([np.broadcast_to(x[:,1:2],(3,3)), np.ones((1,3),dtype=int)]) + // array([[1, 1, 1], [5, 5, 5], [9, 9, 9], [1, 1, 1]]) + // + // ================================================================ + + /// + /// BUG 12a: hstack on column-broadcast arrays duplicates row 0 + /// into row 1. + /// + /// Setup: Two column-vector arrays broadcast to (2,2) and (2,3). + /// hstack should produce (2,5) with distinct rows. + /// + /// NumPy: [[1,1,3,3,3], [2,2,4,4,4]] + /// NumSharp: [[1,1,3,3,3], [1,1,3,3,3]] (row 1 = copy of row 0) + /// + /// The copy routine reads both rows from offset 0 of the broadcast + /// source, ignoring that the column dimension has stride=0 while + /// the row dimension has the original source stride. + /// + [TestMethod] + public void Bug_Hstack_Broadcast_WrongValues() + { + var a = np.broadcast_to(np.array(new int[,] { { 1 }, { 2 } }), new Shape(2, 2)); + var b = np.broadcast_to(np.array(new int[,] { { 3 }, { 4 } }), new Shape(2, 3)); + var r = np.hstack(a, b); + + r.shape.Should().BeEquivalentTo(new[] { 2, 5 }); + + var expected = np.array(new int[,] { { 1, 1, 3, 3, 3 }, { 2, 2, 4, 4, 4 } }); + np.array_equal(r, expected).Should().BeTrue( + "NumPy: hstack(broadcast[[1],[2]]→(2,2), broadcast[[3],[4]]→(2,3)) = " + + "[[1,1,3,3,3],[2,2,4,4,4]]. NumSharp returns [[1,1,3,3,3],[1,1,3,3,3]] — " + + "row 1 is a copy of row 0 because the concatenate copy routine iterates " + + "the broadcast source with a linear offset that doesn't account for the " + + "zero-stride column dimension. Workaround: np.copy() inputs first."); + } + + /// + /// BUG 12b: vstack with sliced+broadcast input shifts row values. + /// + /// Setup: arange(6).reshape(3,2)[:,0:1] → (3,1): [[0],[2],[4]], + /// then broadcast_to (3,3), then vstack with [[10,20,30]]. + /// + /// NumPy: [[0,0,0], [2,2,2], [4,4,4], [10,20,30]] + /// NumSharp: [[0,0,0], [0,0,0], [2,2,2], [10,20,30]] (shifted) + /// + /// Row 1 should be [2,2,2] but shows [0,0,0] (row 0's value). + /// Row 2 should be [4,4,4] but shows [2,2,2] (row 1's value). + /// The iteration is off by one row because the sliced source's + /// row stride is not correctly applied during the copy. + /// + [TestMethod] + public void Bug_Vstack_SlicedBroadcast_WrongValues() + { + var x = np.arange(6).reshape(3, 2); + var col = x[":, 0:1"]; // (3,1): [[0],[2],[4]] + var bcol = np.broadcast_to(col, new Shape(3, 3)); + var other = np.array(new int[,] { { 10, 20, 30 } }); + + var r = np.vstack(bcol, other); + r.shape.Should().BeEquivalentTo(new[] { 4, 3 }); + + r.GetInt32(0, 0).Should().Be(0, "Row 0 should be [0,0,0]"); + r.GetInt32(1, 0).Should().Be(2, + "NumPy: Row 1 should be [2,2,2]. NumSharp returns [0,0,0] — " + + "the concatenate copy routine is off by one row because the sliced " + + "source's row stride (2 elements, skipping column 1) is not correctly " + + "applied during linear iteration of the broadcast array."); + r.GetInt32(2, 0).Should().Be(4, "Row 2 should be [4,4,4]"); + r.GetInt32(3, 0).Should().Be(10, "Row 3 should be [10,20,30]"); + } + + /// + /// BUG 12c: concatenate axis=0 with sliced+broadcast reads garbage. + /// + /// Setup: arange(12).reshape(3,4)[:,1:2] → (3,1): [[1],[5],[9]], + /// then broadcast_to (3,3), then concatenate with ones(1,3). + /// + /// NumPy: [[1,1,1], [5,5,5], [9,9,9], [1,1,1]] + /// NumSharp: [[1,1,1], [5,5,5], [garbage], [1,1,1]] + /// + /// Row 2 should be [9,9,9] but contains garbage values (e.g. + /// 32765, 0, or other values depending on memory state). The + /// sliced column has row stride=4 (stepping over 4-wide rows). + /// The concatenate copy routine doesn't apply this stride, + /// reading from wrong memory offsets for the last row. + /// + [TestMethod] + public void Bug_Concatenate_SlicedBroadcast_WrongValues() + { + var x = np.arange(12).reshape(3, 4); + var col = x[":, 1:2"]; // (3,1): [[1],[5],[9]] + var bcol = np.broadcast_to(col, new Shape(3, 3)); + var other = np.ones(new Shape(1, 3), np.int32); + + var r = np.concatenate(new NDArray[] { bcol, other }, axis: 0); + r.shape.Should().BeEquivalentTo(new[] { 4, 3 }); + + r.GetInt32(0, 0).Should().Be(1, "Row 0 should be [1,1,1]"); + r.GetInt32(1, 0).Should().Be(5, "Row 1 should be [5,5,5]"); + r.GetInt32(2, 0).Should().Be(9, + "NumPy: Row 2 should be [9,9,9]. NumSharp reads garbage values because " + + "the concatenate copy routine doesn't apply the sliced column's row stride " + + "(4 elements per row in the source) when iterating the broadcast array. " + + "The linear offset overshoots and reads from beyond the valid data."); + r.GetInt32(3, 0).Should().Be(1, "Row 3 should be [1,1,1]"); + } + + // ================================================================ + // + // BUG 13: cumsum with axis on broadcast arrays reads garbage memory + // + // SEVERITY: Critical — returns uninitialized memory values. + // + // np.cumsum(broadcast_array, axis=0) returns garbage values + // (uninitialized memory like -1564032936, 32765, etc.) when + // reducing along the non-broadcast axis of a broadcast array. + // + // The bug specifically manifests when axis=0 is the reduction + // axis and the broadcast was along axis=0 (row-broadcast) OR + // when axis=0 reduces a column-broadcast. The key pattern is: + // cumsum reads with wrong strides and accesses memory outside + // the source array's actual data region. + // + // cumsum with axis=1 on row-broadcast: CORRECT + // cumsum with axis=0 on row-broadcast: WRONG (garbage) + // cumsum with axis=0 on col-broadcast: WRONG (garbage) + // cumsum with axis=1 on col-broadcast: WRONG (wrong accumulation) + // cumsum no-axis (flatten first): CORRECT + // + // FIX APPROACH: The cumsum implementation likely uses linear + // pointer iteration along the reduction axis instead of + // coordinate-based access. It should use GetOffset(coords) + // to properly resolve broadcast zero-strides. + // + // PYTHON VERIFICATION: + // >>> a = np.broadcast_to(np.array([1,2,3]), (3,3)) + // >>> np.cumsum(a, axis=0) + // array([[1, 2, 3], [2, 4, 6], [3, 6, 9]]) + // >>> b = np.broadcast_to(np.array([[1],[2],[3]]), (3,3)) + // >>> np.cumsum(b, axis=0) + // array([[1, 1, 1], [3, 3, 3], [6, 6, 6]]) + // >>> np.cumsum(b, axis=1) + // array([[1, 2, 3], [2, 4, 6], [3, 6, 9]]) + // + // ================================================================ + + /// + /// BUG 13a: cumsum axis=0 on row-broadcast returns garbage. + /// + /// Setup: broadcast_to([1,2,3], (3,3)) — each row is [1,2,3]. + /// cumsum(axis=0) should accumulate down columns: row 0 = [1,2,3], + /// row 1 = [2,4,6], row 2 = [3,6,9]. + /// + /// NumPy: [[1,2,3], [2,4,6], [3,6,9]] + /// NumSharp: [[garbage, garbage, garbage], ...] (uninitialized memory) + /// + [TestMethod] + public void Bug_Cumsum_Axis0_RowBroadcast_Garbage() + { + var a = np.broadcast_to(np.array(new int[] { 1, 2, 3 }), new Shape(3, 3)); + var result = np.cumsum(a, 0); + + result.shape.Should().BeEquivalentTo(new[] { 3, 3 }); + + // Row 0 should be the original values + result.GetInt32(0, 0).Should().Be(1, "cumsum axis=0 row 0 col 0"); + result.GetInt32(0, 1).Should().Be(2, "cumsum axis=0 row 0 col 1"); + result.GetInt32(0, 2).Should().Be(3, "cumsum axis=0 row 0 col 2"); + + // Row 1 should be cumulative sum of 2 identical rows + result.GetInt32(1, 0).Should().Be(2, + "NumPy: cumsum(broadcast_to([1,2,3],(3,3)), axis=0)[1,0] = 2 (1+1). " + + "NumSharp returns garbage values (e.g. 32765) because cumsum's axis " + + "iteration reads from uninitialized memory instead of correctly " + + "traversing the broadcast zero-stride dimension."); + result.GetInt32(1, 1).Should().Be(4, "cumsum axis=0 row 1 col 1 = 2+2"); + result.GetInt32(1, 2).Should().Be(6, "cumsum axis=0 row 1 col 2 = 3+3"); + + // Row 2 = sum of 3 identical rows + result.GetInt32(2, 0).Should().Be(3, "cumsum axis=0 row 2 col 0 = 1+1+1"); + result.GetInt32(2, 1).Should().Be(6, "cumsum axis=0 row 2 col 1 = 2+2+2"); + result.GetInt32(2, 2).Should().Be(9, "cumsum axis=0 row 2 col 2 = 3+3+3"); + } + + /// + /// BUG 13b: cumsum axis=0 on column-broadcast returns garbage. + /// + /// Setup: broadcast_to([[1],[2],[3]], (3,3)) — each row is constant. + /// cumsum(axis=0) should accumulate: [1,1,1] → [3,3,3] → [6,6,6]. + /// + /// NumPy: [[1,1,1], [3,3,3], [6,6,6]] + /// NumSharp: [[garbage, garbage, garbage], ...] + /// + [TestMethod] + public void Bug_Cumsum_Axis0_ColBroadcast_Garbage() + { + var col = np.array(new int[,] { { 1 }, { 2 }, { 3 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + var result = np.cumsum(a, 0); + + result.shape.Should().BeEquivalentTo(new[] { 3, 3 }); + + result.GetInt32(0, 0).Should().Be(1, "cumsum axis=0 row 0 = [1,1,1]"); + result.GetInt32(0, 1).Should().Be(1); + result.GetInt32(0, 2).Should().Be(1); + + result.GetInt32(1, 0).Should().Be(3, + "NumPy: cumsum(broadcast_to([[1],[2],[3]],(3,3)), axis=0)[1,0] = 3 (1+2). " + + "NumSharp returns garbage values because the axis=0 iteration reads " + + "memory at wrong offsets for column-broadcast arrays."); + result.GetInt32(1, 1).Should().Be(3); + result.GetInt32(1, 2).Should().Be(3); + + result.GetInt32(2, 0).Should().Be(6, "cumsum axis=0 row 2 = [6,6,6]"); + result.GetInt32(2, 1).Should().Be(6); + result.GetInt32(2, 2).Should().Be(6); + } + + /// + /// BUG 13c: cumsum axis=1 on column-broadcast returns wrong accumulation. + /// + /// Setup: broadcast_to([[1],[2],[3]], (3,3)) — rows are [1,1,1], [2,2,2], [3,3,3]. + /// cumsum(axis=1) should accumulate across each row: + /// row 0: [1, 2, 3] (cumsum of [1,1,1]) + /// row 1: [2, 4, 6] (cumsum of [2,2,2]) + /// row 2: [3, 6, 9] (cumsum of [3,3,3]) + /// + /// NumPy: [[1,2,3], [2,4,6], [3,6,9]] + /// NumSharp: [[3,3,3], [6,6,6], [9,9,9]] + /// + /// The values suggest cumsum reads with wrong strides — it appears + /// to be accumulating along axis=0 instead of axis=1. + /// + [TestMethod] + public void Bug_Cumsum_Axis1_ColBroadcast_Wrong() + { + var col = np.array(new int[,] { { 1 }, { 2 }, { 3 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + var result = np.cumsum(a, 1); + + result.shape.Should().BeEquivalentTo(new[] { 3, 3 }); + + // Row 0: cumsum of [1,1,1] = [1, 2, 3] + result.GetInt32(0, 0).Should().Be(1, "cumsum([1,1,1])[0] = 1"); + result.GetInt32(0, 1).Should().Be(2, + "NumPy: cumsum(axis=1) on column-broadcast row [1,1,1] gives [1,2,3]. " + + "NumSharp returns [3,3,3] — it appears to accumulate along the wrong " + + "axis because the zero-stride column dimension confuses the iteration."); + result.GetInt32(0, 2).Should().Be(3, "cumsum([1,1,1])[2] = 3"); + + // Row 1: cumsum of [2,2,2] = [2, 4, 6] + result.GetInt32(1, 0).Should().Be(2); + result.GetInt32(1, 1).Should().Be(4); + result.GetInt32(1, 2).Should().Be(6); + + // Row 2: cumsum of [3,3,3] = [3, 6, 9] + result.GetInt32(2, 0).Should().Be(3); + result.GetInt32(2, 1).Should().Be(6); + result.GetInt32(2, 2).Should().Be(9); + } + + // ================================================================ + // + // BUG 14: roll on broadcast arrays produces wrong values + // + // SEVERITY: High — silently returns zeros/wrong values. + // + // NDArray.roll(shift, axis) on broadcast arrays produces wrong + // values. The first row may be correct, but subsequent rows + // contain zeros or garbage. Non-broadcast arrays work correctly. + // + // The bug is in the roll implementation's element copy loop, + // which uses linear memory access that doesn't account for + // broadcast zero-strides. When reading source elements to + // shift them, it reads from wrong memory offsets beyond row 0. + // + // row-broadcast (2,3) roll(1, axis=1): + // NumPy: [[3,1,2], [3,1,2]] + // NumSharp: [[3,1,2], [0,0,0]] (row 1 = zeros) + // + // col-broadcast (3,3) roll(1, axis=0): + // NumPy: [[30,30,30], [10,10,10], [20,20,20]] + // NumSharp: [[30,30,30], [0,0,0], [0,0,0]] + // + // PYTHON VERIFICATION: + // >>> np.roll(np.broadcast_to(np.array([1,2,3]), (2,3)), 1, axis=1) + // array([[3, 1, 2], [3, 1, 2]]) + // >>> a = np.broadcast_to(np.array([[10],[20],[30]]), (3,3)) + // >>> np.roll(a, 1, axis=0) + // array([[30, 30, 30], [10, 10, 10], [20, 20, 20]]) + // + // ================================================================ + + /// + /// BUG 14a: roll(shift=1, axis=1) on row-broadcast produces zeros + /// in the second row. + /// + /// Setup: broadcast_to([1,2,3], (2,3)) — both rows are [1,2,3]. + /// roll(1, axis=1) shifts columns right by 1. + /// + /// NumPy: [[3,1,2], [3,1,2]] + /// NumSharp: [[3,1,2], [0,0,0]] + /// + [TestMethod] + public void Bug_Roll_RowBroadcast_ZerosInSecondRow() + { + var a = np.broadcast_to(np.array(new int[] { 1, 2, 3 }), new Shape(2, 3)); + var result = a.roll(1, 1); + + result.shape.Should().BeEquivalentTo(new[] { 2, 3 }); + + // Row 0 happens to be correct + result.GetInt32(0, 0).Should().Be(3, "roll shift=1: last element wraps to front"); + result.GetInt32(0, 1).Should().Be(1); + result.GetInt32(0, 2).Should().Be(2); + + // Row 1 should be identical (same source data) + result.GetInt32(1, 0).Should().Be(3, + "NumPy: roll(broadcast_to([1,2,3],(2,3)), 1, axis=1) row 1 = [3,1,2]. " + + "NumSharp returns [0,0,0] because the roll implementation reads row 1's " + + "source data using linear offset that lands in zeroed memory instead of " + + "the broadcast zero-stride repeated row."); + result.GetInt32(1, 1).Should().Be(1); + result.GetInt32(1, 2).Should().Be(2); + } + + /// + /// BUG 14b: roll(shift=1, axis=0) on column-broadcast produces zeros. + /// + /// Setup: broadcast_to([[10],[20],[30]], (3,3)). + /// roll(1, axis=0) shifts rows down by 1. + /// + /// NumPy: [[30,30,30], [10,10,10], [20,20,20]] + /// NumSharp: [[30,30,30], [0,0,0], [0,0,0]] + /// + [TestMethod] + public void Bug_Roll_ColBroadcast_ZerosAfterFirstRow() + { + var col = np.array(new int[,] { { 10 }, { 20 }, { 30 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + var result = a.roll(1, 0); + + result.shape.Should().BeEquivalentTo(new[] { 3, 3 }); + + // Row 0 = last original row (30) shifted to front + result.GetInt32(0, 0).Should().Be(30, "roll shift=1 axis=0: last row wraps to front"); + result.GetInt32(0, 1).Should().Be(30); + result.GetInt32(0, 2).Should().Be(30); + + // Row 1 = original row 0 (10) + result.GetInt32(1, 0).Should().Be(10, + "NumPy: roll(broadcast_to([[10],[20],[30]],(3,3)), 1, axis=0)[1] = [10,10,10]. " + + "NumSharp returns [0,0,0] because the roll implementation reads with " + + "linear offsets that don't account for the column-broadcast zero-strides."); + result.GetInt32(1, 1).Should().Be(10); + result.GetInt32(1, 2).Should().Be(10); + + // Row 2 = original row 1 (20) + result.GetInt32(2, 0).Should().Be(20); + result.GetInt32(2, 1).Should().Be(20); + result.GetInt32(2, 2).Should().Be(20); + } + + // ================================================================ + // + // BUG 15: sum/mean/var/std with axis=0 on column-broadcast + // returns wrong values (under-counts rows) + // + // SEVERITY: Critical — silently returns wrong computation results. + // + // When reducing along axis=0 on a column-broadcast array, the + // reduction functions (sum, mean, var, std) produce wrong values. + // The sum appears to read fewer elements than the actual number + // of rows, and since mean/var/std all depend on sum, they are + // all affected. + // + // The bug pattern: + // - Row-broadcast + axis=0: CORRECT + // - Row-broadcast + axis=1: CORRECT + // - Col-broadcast + axis=1: CORRECT + // - Col-broadcast + axis=0: WRONG ← the buggy combination + // + // This suggests the reduction's axis=0 iteration uses strides + // that are wrong for the column-broadcast memory layout. The + // iteration likely uses the physical stride (which is the + // original column source's row stride) instead of the broadcast + // shape's row stride. + // + // Examples: + // broadcast_to([[100],[200],[300]], (3,3)): + // sum(axis=0) = [300,300,300] instead of [600,600,600] + // (sums to 100+200=300 instead of 100+200+300=600) + // + // broadcast_to([[1],[2],[3],[4],[5]], (5,3)): + // sum(axis=0) = [7,7,7] instead of [15,15,15] + // + // sum no-axis (flattens first): CORRECT → the flatten path handles + // broadcast correctly for the total sum, but the axis reduction path + // doesn't. + // + // PYTHON VERIFICATION: + // >>> a = np.broadcast_to(np.array([[100],[200],[300]]), (3,3)) + // >>> np.sum(a, axis=0) + // array([600, 600, 600]) + // >>> np.mean(a.astype(float), axis=0) + // array([200., 200., 200.]) + // >>> b = np.broadcast_to(np.array([[1.],[2.],[3.]]), (3,3)) + // >>> np.var(b, axis=0) + // array([0.66666667, 0.66666667, 0.66666667]) + // >>> np.std(b, axis=0) + // array([0.81649658, 0.81649658, 0.81649658]) + // + // ================================================================ + + /// + /// BUG 15a: sum(axis=0) on column-broadcast returns wrong totals. + /// + /// Setup: broadcast_to([[100],[200],[300]], (3,3)). + /// Each column should sum to 100+200+300=600. + /// + /// NumPy: [600, 600, 600] + /// NumSharp: [300, 300, 300] (under-counts, appears to miss row 2) + /// + [TestMethod] + public void Bug_Sum_Axis0_ColBroadcast_WrongValues() + { + var col = np.array(new int[,] { { 100 }, { 200 }, { 300 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + var result = np.sum(a, 0); + + result.shape.Should().BeEquivalentTo(new[] { 3 }); + + result.GetInt32(0).Should().Be(600, + "NumPy: sum(broadcast_to([[100],[200],[300]],(3,3)), axis=0) = [600,600,600]. " + + "NumSharp returns [300,300,300] — only summing 2 of 3 rows (100+200=300). " + + "The axis=0 reduction iteration uses wrong strides for column-broadcast arrays, " + + "causing it to read fewer rows than exist in the broadcast shape."); + result.GetInt32(1).Should().Be(600); + result.GetInt32(2).Should().Be(600); + } + + /// + /// BUG 15b: mean(axis=0) on column-broadcast returns wrong average. + /// Cascades from Bug 15a (sum is wrong → mean = sum/count is wrong). + /// + /// NumPy: mean = [2.0, 2.0, 2.0] + /// NumSharp: mean = [1.0, 1.0, 1.0] + /// + [TestMethod] + public void Bug_Mean_Axis0_ColBroadcast_WrongValues() + { + var col = np.array(new double[,] { { 1.0 }, { 2.0 }, { 3.0 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + var result = np.mean(a, 0); + + result.shape.Should().BeEquivalentTo(new[] { 3 }); + + result.GetDouble(0).Should().BeApproximately(2.0, 1e-10, + "NumPy: mean(broadcast_to([[1],[2],[3]],(3,3)), axis=0) = [2,2,2]. " + + "NumSharp returns [1,1,1] because the underlying sum is wrong (Bug 15a), " + + "giving sum=3 instead of sum=6, so mean=3/3=1 instead of 6/3=2."); + result.GetDouble(1).Should().BeApproximately(2.0, 1e-10); + result.GetDouble(2).Should().BeApproximately(2.0, 1e-10); + } + + /// + /// BUG 15c: var(axis=0) on column-broadcast returns wrong variance. + /// + /// NumPy: var = [0.6667, 0.6667, 0.6667] + /// NumSharp: var = [0.0, 0.0, 0.0] + /// + [TestMethod] + public void Bug_Var_Axis0_ColBroadcast_WrongValues() + { + var col = np.array(new double[,] { { 1.0 }, { 2.0 }, { 3.0 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + var result = np.var(a, 0); + + result.shape.Should().BeEquivalentTo(new[] { 3 }); + + result.GetDouble(0).Should().BeApproximately(2.0 / 3.0, 1e-10, + "NumPy: var(broadcast_to([[1],[2],[3]],(3,3)), axis=0) = [0.667,0.667,0.667]. " + + "NumSharp returns [0,0,0] because the wrong mean (Bug 15b) cascades: " + + "mean=1 makes each deviation wrong, producing incorrect variance."); + result.GetDouble(1).Should().BeApproximately(2.0 / 3.0, 1e-10); + result.GetDouble(2).Should().BeApproximately(2.0 / 3.0, 1e-10); + } + + /// + /// BUG 15d: std(axis=0) on column-broadcast returns wrong std deviation. + /// + /// NumPy: std = [0.8165, 0.8165, 0.8165] + /// NumSharp: std = [0.0, 0.0, 0.0] + /// + [TestMethod] + public void Bug_Std_Axis0_ColBroadcast_WrongValues() + { + var col = np.array(new double[,] { { 1.0 }, { 2.0 }, { 3.0 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + var result = np.std(a, 0); + + result.shape.Should().BeEquivalentTo(new[] { 3 }); + + var expected_std = Math.Sqrt(2.0 / 3.0); + result.GetDouble(0).Should().BeApproximately(expected_std, 1e-10, + "NumPy: std(broadcast_to([[1],[2],[3]],(3,3)), axis=0) = [0.8165,...]. " + + "NumSharp returns [0,0,0]. Root cause: sum(axis=0) is wrong (Bug 15a), " + + "which cascades through mean → var → std."); + result.GetDouble(1).Should().BeApproximately(expected_std, 1e-10); + result.GetDouble(2).Should().BeApproximately(expected_std, 1e-10); + } + + /// + /// BUG 15e: sum(axis=0) on a larger column-broadcast (5,3) returns wrong totals. + /// Verifies the bug scales with array size (not just 3x3). + /// + /// NumPy: sum(axis=0) = [15, 15, 15] (1+2+3+4+5) + /// NumSharp: sum(axis=0) = [7, 7, 7] + /// + [TestMethod] + public void Bug_Sum_Axis0_ColBroadcast_5x3_WrongValues() + { + var col = np.array(new int[,] { { 1 }, { 2 }, { 3 }, { 4 }, { 5 } }); + var a = np.broadcast_to(col, new Shape(5, 3)); + var result = np.sum(a, 0); + + result.shape.Should().BeEquivalentTo(new[] { 3 }); + + result.GetInt32(0).Should().Be(15, + "NumPy: sum(broadcast_to([[1],[2],[3],[4],[5]],(5,3)), axis=0) = [15,15,15]. " + + "NumSharp returns [7,7,7]. The wrong value (7 instead of 15) confirms the " + + "axis=0 reduction iteration reads wrong memory offsets for column-broadcast."); + result.GetInt32(1).Should().Be(15); + result.GetInt32(2).Should().Be(15); + } + + // ================================================================ + // + // BUG 16: argsort crashes on any 2D array (not broadcast-specific) + // + // SEVERITY: High — the function is non-functional for 2D+. + // + // NDArray.argsort() throws InvalidOperationException: + // "Failed to compare two elements in the array" for ANY 2D + // array, whether broadcast or not. The function works correctly + // for 1D arrays. + // + // This is NOT a broadcast-specific bug. It affects all 2D arrays. + // The root cause is likely that the internal comparison function + // used for sorting doesn't handle multi-dimensional data correctly + // — it may be comparing elements across rows instead of within + // each row (axis=-1 default). + // + // PYTHON VERIFICATION: + // >>> np.argsort(np.array([[3,1,2],[6,4,5]])) + // array([[1, 2, 0], [1, 2, 0]]) + // >>> np.argsort(np.array([[3,1,2],[6,4,5]]), axis=0) + // array([[0, 0, 0], [1, 1, 1]]) + // + // ================================================================ + + /// + /// BUG 16a: argsort crashes on any 2D int array. + /// + /// NumPy: argsort([[3,1,2],[6,4,5]]) = [[1,2,0],[1,2,0]] + /// NumSharp: InvalidOperationException: Failed to compare two elements + /// + [TestMethod] + public void Bug_Argsort_2D_Crashes() + { + var a = np.array(new int[,] { { 3, 1, 2 }, { 6, 4, 5 } }); + + NDArray result = null; + new Action(() => result = a.argsort()) + .Should().NotThrow( + "NumPy: argsort([[3,1,2],[6,4,5]]) returns [[1,2,0],[1,2,0]]. " + + "NumSharp throws InvalidOperationException: 'Failed to compare two " + + "elements in the array' for ANY 2D array. The comparison function " + + "used in the internal sort doesn't handle multi-dimensional indexing."); + + result.Should().NotBeNull(); + result.shape.Should().BeEquivalentTo(new[] { 2, 3 }); + + // Row 0: argsort of [3,1,2] = [1,2,0] + result.GetInt32(0, 0).Should().Be(1, "index of min(3,1,2)=1 is at position 1"); + result.GetInt32(0, 1).Should().Be(2, "index of 2 is at position 2"); + result.GetInt32(0, 2).Should().Be(0, "index of max(3,1,2)=3 is at position 0"); + + // Row 1: argsort of [6,4,5] = [1,2,0] + result.GetInt32(1, 0).Should().Be(1, "index of min(6,4,5)=4 is at position 1"); + result.GetInt32(1, 1).Should().Be(2, "index of 5 is at position 2"); + result.GetInt32(1, 2).Should().Be(0, "index of max(6,4,5)=6 is at position 0"); + } + + /// + /// BUG 16b: argsort crashes on 2D double array too. + /// Confirms the bug is type-independent. + /// + /// NumPy: argsort([[3.0,1.0,2.0]]) = [[1,2,0]] + /// NumSharp: InvalidOperationException + /// + [TestMethod] + public void Bug_Argsort_2D_Double_Crashes() + { + var a = np.array(new double[,] { { 3.0, 1.0, 2.0 } }); + + NDArray result = null; + new Action(() => result = a.argsort()) + .Should().NotThrow( + "NumPy: argsort([[3.0,1.0,2.0]]) returns [[1,2,0]]. " + + "NumSharp throws InvalidOperationException for any 2D array, " + + "regardless of dtype."); + + result.Should().NotBeNull(); + result.GetInt32(0, 0).Should().Be(1); + result.GetInt32(0, 1).Should().Be(2); + result.GetInt32(0, 2).Should().Be(0); + } + + // ================================================================ + // + // BUG 4 VARIANT: np.clip on broadcast throws NotSupportedException + // + // Same root cause as Bug 4 — the IsBroadcasted guard in + // Broadcast(Shape, Shape) blocks the operation. Clip internally + // broadcasts the input with the min/max bounds, hitting the guard. + // + // Documented here as a test case but not a new distinct bug. + // + // PYTHON VERIFICATION: + // >>> np.clip(np.broadcast_to(np.array([1.,5.,9.]), (2,3)), 2., 7.) + // array([[2., 5., 7.], [2., 5., 7.]]) + // + // ================================================================ + + /// + /// BUG 4 VARIANT: np.clip on broadcast array throws because clip + /// internally broadcasts and hits the IsBroadcasted guard. + /// + /// NumPy: clip(broadcast, 2, 7) = [[2,5,7],[2,5,7]] + /// NumSharp: NotSupportedException: Unable to broadcast already broadcasted shape. + /// + [TestMethod] + public void Bug_Clip_Broadcast_ThrowsNotSupported() + { + var a = np.broadcast_to(np.array(new double[] { 1.0, 5.0, 9.0 }), new Shape(2, 3)); + + NDArray result = null; + new Action(() => result = np.clip(a, 2.0, 7.0)) + .Should().NotThrow( + "NumPy: clip(broadcast_to([1,5,9],(2,3)), 2, 7) = [[2,5,7],[2,5,7]]. " + + "NumSharp throws NotSupportedException: 'Unable to broadcast already " + + "broadcasted shape' — same IsBroadcasted guard as Bug 4. Clip internally " + + "broadcasts the input to apply min/max, hitting the guard."); + + result.Should().NotBeNull(); + result.shape.Should().BeEquivalentTo(new[] { 2, 3 }); + result.GetDouble(0, 0).Should().Be(2.0); + result.GetDouble(0, 1).Should().Be(5.0); + result.GetDouble(0, 2).Should().Be(7.0); + } + + // ================================================================ + // + // BUG 17: Slicing a broadcast array produces a view with + // mismatched strides vs data layout (ROOT CAUSE) + // + // SEVERITY: Critical — this is the ROOT CAUSE of bugs 1, 11, + // 12, 13, 14, and 15. Every operation that slices a broadcast + // array hits this code path. + // + // LOCATION: UnmanagedStorage.Slicing.cs, GetViewInternal(), + // lines 100-101: + // + // if (_shape.IsBroadcasted) + // return Clone().Alias(_shape.Slice(slices)); + // + // WHAT HAPPENS: + // + // 1. Clone() correctly materializes the broadcast data into + // contiguous memory. For broadcast_to([[100],[200],[300]], + // (3,3)), CloneData() uses MultiIterator.Assign to produce + // 9 elements in row-major order: + // [100, 100, 100, 200, 200, 200, 300, 300, 300] + // This contiguous (3,3) data has implicit strides [3, 1]. + // + // 2. _shape.Slice(slices) computes the sliced shape, but _shape + // is the ORIGINAL BROADCAST shape with strides [1, 0]. The + // resulting ViewInfo.OriginalShape inherits these broadcast + // strides [1, 0]. + // + // 3. .Alias() attaches this broadcast-strided shape to the + // contiguous data. Now there is a MISMATCH: the data is + // laid out with strides [3, 1] but the shape claims [1, 0]. + // + // 4. When GetOffset computes memory offsets (for GetInt32, + // iterators, reductions, etc.), it uses the broadcast + // strides [1, 0] from ViewInfo.OriginalShape. For a[:, 0]: + // GetOffset(0) = 0*1 + 0*0 = 0 → data[0] = 100 ✓ + // GetOffset(1) = 1*1 + 0*0 = 1 → data[1] = 100 ✗ + // GetOffset(2) = 2*1 + 0*0 = 2 → data[2] = 100 ✗ + // But the correct contiguous offsets should be: + // GetOffset(0) = 0*3 + 0*1 = 0 → data[0] = 100 ✓ + // GetOffset(1) = 1*3 + 0*1 = 3 → data[3] = 200 ✓ + // GetOffset(2) = 2*3 + 0*1 = 6 → data[6] = 300 ✓ + // + // PROOF: np.copy(a)[:, 0] returns [100, 200, 300] (correct) + // because np.copy creates a clean contiguous shape with strides + // [3, 1]. The direct slice a[:, 0] returns [100, 100, 100] + // because the shape retains broadcast strides [1, 0]. + // + // The non-broadcast code path (line 103) does NOT have this + // problem because Alias(_shape.Slice(slices)) reuses the + // SAME memory (no Clone), so the strides in the OriginalShape + // match the actual data layout. + // + // WHY THIS IS THE ROOT CAUSE OF MANY BUGS: + // + // Every operation that indexes into a broadcast array goes + // through GetViewInternal and hits line 101. The returned + // storage has cloned contiguous data but broadcast strides: + // + // Bug 1: ToString iterates the sliced broadcast view + // Bug 11: flatten() iterates the sliced broadcast view + // Bug 12: concatenate copies from sliced broadcast views + // Bug 13: cumsum slices along axis on broadcast arrays + // Bug 14: roll slices along axis on broadcast arrays + // Bug 15: sum/mean/var/std axis reduction slices each + // "lane" via arr[slices] on the broadcast array + // + // In all cases, the reduction/iteration code does arr[slices] + // which calls GetViewInternal, which clones the data to + // contiguous layout but keeps broadcast strides, and then + // iteration reads from wrong memory offsets. + // + // PYTHON VERIFICATION: + // >>> a = np.broadcast_to(np.array([[100],[200],[300]]), (3,3)) + // >>> a[:, 0] + // array([100, 200, 300]) + // >>> a[0, :] + // array([100, 100, 100]) + // >>> a[:, 0].sum() + // 600 + // + // ================================================================ + + /// + /// BUG 17a: Slicing a column-broadcast array with [:, N] reads + /// the same row's value for every row. + /// + /// The slice a[:, 0] on broadcast_to([[100],[200],[300]], (3,3)) + /// should return [100, 200, 300] (column 0 of each row). + /// Instead it returns [100, 100, 100] — row 0's value repeated. + /// + /// Root cause: GetViewInternal clones the data to contiguous + /// layout [3, 1] but the shape retains broadcast strides [1, 0]. + /// GetOffset computes offsets 0, 1, 2 (using stride 1) instead + /// of 0, 3, 6 (using stride 3), so it reads data[0..2] which + /// are all 100 (row 0 repeated 3 times in the contiguous clone). + /// + [TestMethod] + public void Bug_SliceBroadcast_StrideMismatch_ColumnBroadcast_SliceColumn() + { + // Setup: column vector [[100],[200],[300]] broadcast to (3,3) + var col = np.array(new int[,] { { 100 }, { 200 }, { 300 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + + // Verify the broadcast array itself is correct via coordinate access + a.GetInt32(0, 0).Should().Be(100, "broadcast[0,0] = 100"); + a.GetInt32(1, 0).Should().Be(200, "broadcast[1,0] = 200"); + a.GetInt32(2, 0).Should().Be(300, "broadcast[2,0] = 300"); + + // Slice a[:, 0] — should extract column 0: [100, 200, 300] + var sliced = a[":, 0"]; + sliced.shape.Should().BeEquivalentTo(new[] { 3 }); + + // These assertions reproduce the bug: + // NumPy returns [100, 200, 300]. NumSharp returns [100, 100, 100]. + sliced.GetInt32(0).Should().Be(100, + "a[:, 0][0] should be 100 (row 0, col 0)."); + sliced.GetInt32(1).Should().Be(200, + "a[:, 0][1] should be 200 (row 1, col 0). " + + "NumSharp returns 100 because GetViewInternal clones the broadcast " + + "data to contiguous layout (strides [3,1]) but the shape retains " + + "broadcast strides [1,0]. GetOffset(1) computes 1*1=1 instead of " + + "1*3=3, reading data[1]=100 (still row 0) instead of data[3]=200."); + sliced.GetInt32(2).Should().Be(300, + "a[:, 0][2] should be 300 (row 2, col 0). " + + "NumSharp returns 100 because GetOffset(2) computes 2*1=2 instead " + + "of 2*3=6, reading data[2]=100 (still row 0) instead of data[6]=300."); + } + + /// + /// BUG 17b: np.copy produces correct results, proving the data + /// materialization itself works — only the stride assignment is wrong. + /// + /// np.copy creates a new contiguous array with clean strides [3,1], + /// so slicing the copy works correctly. This proves Clone()/CloneData() + /// correctly materializes the data; the bug is purely that + /// _shape.Slice(slices) attaches broadcast strides to the clone. + /// + [TestMethod] + public void Bug_SliceBroadcast_CopyWorkaround_Proves_StrideMismatch() + { + var col = np.array(new int[,] { { 100 }, { 200 }, { 300 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + + // np.copy materializes with clean contiguous shape [3, 1] + var copy = np.copy(a); + copy.strides.Should().BeEquivalentTo(new[] { 3, 1 }, + "np.copy produces contiguous strides [3,1]"); + copy.Shape.IsBroadcasted.Should().BeFalse( + "np.copy clears broadcast flag"); + + // Slicing the copy works correctly + var copySliced = copy[":, 0"]; + copySliced.GetInt32(0).Should().Be(100); + copySliced.GetInt32(1).Should().Be(200); + copySliced.GetInt32(2).Should().Be(300); + + // Direct slice of broadcast gives wrong values + var directSliced = a[":, 0"]; + directSliced.GetInt32(0).Should().Be(100); + directSliced.GetInt32(1).Should().Be(200, + "Direct slice a[:, 0][1] must equal np.copy(a)[:, 0][1] = 200. " + + "Both paths clone the same data; the difference is that np.copy " + + "creates clean strides [3,1] while GetViewInternal keeps broadcast " + + "strides [1,0] from _shape.Slice(slices). The data is identical, " + + "only the offset calculation differs."); + directSliced.GetInt32(2).Should().Be(300, + "Direct slice a[:, 0][2] must equal np.copy(a)[:, 0][2] = 300."); + } + + /// + /// BUG 17c: Sliced-then-broadcast array: slicing triggers + /// memory corruption (Debug.Assert: index < Count). + /// + /// Setup: arange(12).reshape(3,4)[:,1:2] gives a (3,1) column + /// with values [[1],[5],[9]] and row stride=4 (stepping over + /// 4-wide rows). Broadcast to (3,3) then slice b[:, 0]. + /// + /// The source column has compound strides from the reshape+slice. + /// After Clone, data is 9 contiguous elements. But the shape + /// retains the compound sliced+broadcast strides. GetOffset + /// computes offsets using the original row stride (4), which + /// overshoots the 9-element cloned buffer, triggering + /// Debug.Assert("index < Count, Memory corruption expected"). + /// + /// This is the same root cause as 17a but with a more severe + /// manifestation: instead of just reading wrong values, the + /// wrong strides cause out-of-bounds memory access. + /// + /// We use np.copy as the control path: copy materializes with + /// clean strides, then slicing works correctly. + /// + [TestMethod] + public void Bug_SliceBroadcast_StrideMismatch_SlicedSourceRows() + { + // arange(12).reshape(3,4) = [[ 0, 1, 2, 3], + // [ 4, 5, 6, 7], + // [ 8, 9,10,11]] + // Slice column 1: [:,1:2] = [[1],[5],[9]] + // Broadcast to (3,3): [[1,1,1],[5,5,5],[9,9,9]] + var x = np.arange(12).reshape(3, 4); + var col = x[":, 1:2"]; // (3,1): [[1],[5],[9]] + var b = np.broadcast_to(col, new Shape(3, 3)); + + // Coordinate access on the broadcast is correct + b.GetInt32(0, 0).Should().Be(1); + b.GetInt32(1, 0).Should().Be(5); + b.GetInt32(2, 0).Should().Be(9); + + // np.copy + slice works correctly (control path) + var copySliced = np.copy(b)[":, 0"]; + copySliced.GetInt32(0).Should().Be(1, "copy[:, 0][0] = 1 (control)"); + copySliced.GetInt32(1).Should().Be(5, "copy[:, 0][1] = 5 (control)"); + copySliced.GetInt32(2).Should().Be(9, "copy[:, 0][2] = 9 (control)"); + + // Direct slice should give the same result but crashes: + // GetViewInternal clones data to 9 contiguous elements but + // attaches compound strides from the sliced+broadcast shape. + // GetOffset computes offsets using original row stride (4), + // which overshoots the 9-element buffer → Debug.Assert fires. + // + // This try-catch is necessary because Debug.Assert throws a + // DebugAssertException that the test platform translates to a + // process-level failure, bypassing normal exception handling. + try + { + var sliced = b[":, 0"]; + // If it doesn't throw, verify values are correct + sliced.GetInt32(0).Should().Be(1, + "b[:, 0][0] should be 1 (row 0 value)."); + sliced.GetInt32(1).Should().Be(5, + "b[:, 0][1] should be 5 (row 1 value)."); + sliced.GetInt32(2).Should().Be(9, + "b[:, 0][2] should be 9 (row 2 value)."); + } + catch (Exception ex) + { + Assert.Fail( + $"Slicing b[:, 0] on a sliced+broadcast array must not throw. " + + $"Threw {ex.GetType().Name}: {ex.Message}. " + + $"Root cause: GetViewInternal clones data to contiguous layout " + + $"but attaches sliced+broadcast strides, causing out-of-bounds " + + $"offset computation. The np.copy(b)[:, 0] control path returns " + + $"the correct values [1, 5, 9]."); + } + } + + /// + /// BUG 17d: The stride mismatch directly causes Bug 15a — + /// sum(axis=0) on column-broadcast returns 300 instead of 600. + /// + /// The sum reduction does arr[Slice.All, Slice.Index(col)] for + /// each output column. This calls GetViewInternal, which clones + /// the data but keeps broadcast strides. The resulting 1D slice + /// reads [100, 100, 100] instead of [100, 200, 300], so + /// sum = 300 instead of 600. + /// + /// This test isolates the exact mechanism: it manually performs + /// the same slice that the reduction code does, proving that the + /// slice itself returns wrong values. + /// + [TestMethod] + public void Bug_SliceBroadcast_StrideMismatch_Causes_Sum_Axis0_Bug() + { + var col = np.array(new int[,] { { 100 }, { 200 }, { 300 } }); + var a = np.broadcast_to(col, new Shape(3, 3)); + + // This is exactly what the axis=0 reduction does internally: + // For each column, it slices arr[Slice.All, Slice.Index(col)] + // to get a 1D vector along axis 0, then sums it. + for (int c = 0; c < 3; c++) + { + var lane = a[$":, {c}"]; + lane.size.Should().Be(3, $"lane for col {c} should have 3 elements"); + + // The lane should contain [100, 200, 300] for every column + // (because the column dimension is broadcast — all columns are identical) + var laneValues = new int[3]; + for (int r = 0; r < 3; r++) + laneValues[r] = lane.GetInt32(r); + + var laneSum = laneValues.Sum(); + laneSum.Should().Be(600, + $"Sum of lane a[:, {c}] should be 100+200+300=600. " + + $"Got values [{string.Join(", ", laneValues)}] summing to {laneSum}. " + + "The reduction reads [100, 100, 100] (sum=300) because the slice " + + "has broadcast strides [1,0] on contiguous data [3,1]. " + + "GetOffset maps rows 0,1,2 to memory offsets 0,1,2 instead of " + + "0,3,6, so all three reads land in row 0's data region."); + } + } + } +} diff --git a/test/NumSharp.UnitTest/Utilities/SteppingOverArray.cs b/test/NumSharp.UnitTest/Utilities/SteppingOverArray.cs index 4c18a47a5..4db84c92e 100644 --- a/test/NumSharp.UnitTest/Utilities/SteppingOverArray.cs +++ b/test/NumSharp.UnitTest/Utilities/SteppingOverArray.cs @@ -33,7 +33,7 @@ public void Stepping() //>>> var a = new[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Assert.AreSame(a, a.Step(1)); - AssertAreEqual(new[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.Reverse().ToArray(), a.Step(-1)); + AssertAreEqual(new[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.AsEnumerable().Reverse().ToArray(), a.Step(-1)); AssertAreEqual(new[] {10, 8, 6, 4, 2,}.ToArray(), a.Step(-2)); AssertAreEqual(new[] {1, 3, 5, 7, 9,}.ToArray(), a.Step(2)); AssertAreEqual(new[] {10, 7, 4, 1,}.ToArray(), a.Step(-3));