Table of Contents

Namespace NumSharp.Utilities

Classes

ArrayConvert

Presents all possible combinations of array conversion of types supported by numpy.

Arrays
ArraysExtensions
ConcurrentHashset<T>
Converts

Provides various methods related to Convert.

Converts<T>

Provides various methods related to Convert based on give T.

Hashset<T>

Implementation notes: This uses an array-based implementation similar to Dictionary<T>, using a buckets array to map hash values to the Slots array. Items in the Slots array that hash to the same value are chained together through the "next" indices.

This implementation supports long indexing for collections exceeding int.MaxValue elements.

The capacity is always prime; so during resizing, the capacity is chosen as the next prime greater than double the last capacity (or 33% growth for very large sets above 1 billion elements).

The underlying data structures are lazily initialized. Because of the observation that, in practice, hashtables tend to contain only a few elements, the initial capacity is set very small (3 elements) unless the ctor with a collection is used.

The +/- 1 modifications in methods that add, check for containment, etc allow us to distinguish a hash code of 0 from an uninitialized bucket. This saves us from having to reset each bucket to -1 when resizing. See Contains, for example.

Set methods such as UnionWith, IntersectWith, ExceptWith, and SymmetricExceptWith modify this set.

Some operations can perform faster if we can assume "other" contains unique elements according to this equality comparer. The only times this is efficient to check is if other is a hashset. Note that checking that it's a hashset alone doesn't suffice; we also have to check that the hashset is using the same equality comparer. If other has a different equality comparer, it will have unique elements according to its own equality comparer, but not necessarily according to ours. Therefore, to go these optimized routes we check that other is a hashset using the same equality comparer.

A HashSet with no elements has the properties of the empty set. (See IsSubset, etc. for special empty set checks.)

A couple of methods have a special case if other is this (e.g. SymmetricExceptWith). If we didn't have these checks, we could be iterating over the set and modifying at the same time.

InfoOf

Static utility methods for type information.

InfoOf<T>

Provides a cache for properties of T that requires computation.

NDComplexMath

Complex-math helpers backing NumSharp's complex (complex128) unary ufuncs. Each entry point reproduces NumPy 2.4.2 bit-for-bit (or within 3 ULP on the finite interior), verified by a 504-point bit-exact sweep and a layout sweep (contiguous / F-contiguous / strided / transposed / reversed / sliced / broadcast / 0-d). Most are direct ports of NumPy's own routines in npy_math_complex.c.src (the FreeBSD msun implementations) because System.Numerics.Complex diverges on large magnitudes, the unit circle, tiny/subnormal values, branch cuts, and signed zeros.

Ported NumPy algorithms. Log(Complex) = npy_clog (four-regime rescale incl. the near-|z|=1 log1p path; drives Log10(Complex) and the engine's log2); Sinh(Complex)/ Cosh(Complex) = textbook sinh/cosh(x)·trig(y) with a y==0 guard (so a huge real part doesn't become inf·0 = NaN) and the C99 Annex G non-finite tables; Tanh(Complex) = Kahan's npy_ctanh (markedly more accurate than the BCL near ±π/2) + the |x|≥22 overflow-safe branch; Sin(Complex)/Cos(Complex)/ Tan(Complex) route through those exactly as NumPy defines csin/ccos/ctan; Atan(Complex) = the full npy_catanh (real atanh/atan on the axes, the log1p interior, and an exponent-classified real_part_reciprocal); Exp(Complex) = npy_cexp; Sqrt(Complex) = npy_csqrt; Expm1(Complex) = nc_expm1 with a Goldberg real expm1; Square(Complex) = FMA-contracted z·z (matches NumPy's complex multiply overflow/cancellation); Reciprocal(Complex) = Smith's nc_recip; Exp2(Complex)/Log1p(Complex) compose the above; Abs(Complex) = npy_cabs (C99 hypot: an infinite component yields +inf even alongside a NaN — the .NET 8 Complex.Abs returns NaN there).

Still delegating to the BCL (at parity): Asin(Complex) and Acos(Complex) use Asin(Complex)/Acos(Complex) on the finite interior with signed-zero / branch-cut fixups, and the C99 non-finite tables otherwise.

Accepted residuals (pathological inputs only, beyond 3 ULP): cos/sin with a NaN imaginary part pick the C99-unspecified sign for the resulting zero; arccos with a sub-DBL_MIN imaginary part flushes the denormal real part to 0 where NumPy's cacos hard-work kernel keeps it (~5.8e-309); sinh/cosh at the |x|∈[710,710.13] overflow edge differ because Windows' CRT sinh overflows where .NET's stays finite.

Perf: each public entry point is a tiny finite-path wrapper marked AggressiveInlining so the JIT folds it into the IL-emitted unary kernel (no per-element call frame); the rare non-finite / special-value tables live in cold helpers marked AggressiveOptimization (kept out-of-line so the hot wrapper stays inlineable, fully optimized when hit). A benchmark of an IL-inlined variant vs this call-based form showed the per-element cost is dominated by the transcendental, so hand-emitting the formulas is not worth the duplication.

NDCoordinatesAxisIncrementor
NDCoordinatesIncrementor
NDCoordinatesIncrementorAutoResetting
NDCoordinatesLeftToAxisIncrementor
NDDivision

floor-division and remainder helpers matching NumPy's floor_div_@TYPE@ (loops_arithmetic.dispatch.c.src), integer remainder (loops_modulo.dispatch.c.src), and the floating-point npy_floor_divide@c@ / npy_remainder@c@ (Python-divmod port in npy_math_internal.h.src).

Semantics replicated exactly:

  • Integer divide/modulo by zero returns 0 (NumPy raises a RuntimeWarning but yields 0, never throwing — C#'s DivideByZeroException must not surface).
  • Signed integer floor-division rounds toward negative infinity (Python //), not toward zero like C# /; MIN // -1 wraps to MIN (overflow), matching NumPy's npy_set_floatstatus_overflow(); return NPY_MIN.
  • Signed integer remainder uses the floored (Python) sign convention: the result has the sign of the divisor; MIN % -1 == 0.
  • Float floor-division/modulo follow CPython's divmod (fmod, sign-fixup, snap-to-nearest-integer), so a // 0.0 is ±inf/nan (not forced NaN) and edge cases like 0.7 // 0.1 == 6.0 and -2.0 // inf == -1.0 match.
NDExtendedCoordinatesIncrementor
NDIntegerPower

Integer power helpers matching NumPy's @TYPE@_power loop in loops.c.src. Uses repeated-squaring with native dtype wraparound (e.g. uint8 ** 8 = 0).

These helpers assume the exponent is non-negative. NumPy raises ValueError("Integers to negative integer powers are not allowed.") for any negative integer exponent, regardless of base value; the caller is responsible for that pre-check (see DefaultEngine.Power).

NDOffsetIncrementor
NDOffsetIncrementorAutoresetting
NonGenericConvert

Provides a way to convert boxed object from known time to specific type.

NpFunc
NumberInfo
SteppingExtension
TypelessConvert

Provides a way to convert boxed object from known input type to known output type. By making it receive and return object - It is suitable for a common delegate: see TypelessConvertDelegate

UnmanagedBuffer

Provides low-level memory copy operations for unmanaged types.

UnmanagedSpanExtensions

Extension methods for UnmanagedSpan{T} and ReadOnlyUnmanagedSpan{T}. These provide SIMD-accelerated operations where possible. All methods support full 64-bit indexing natively.

py

Implements Python utility functions that are often used in connection with numpy

Structs

Hashset<T>.Enumerator
ReadOnlyUnmanagedSpan<T>

ReadOnlyUnmanagedSpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed or native memory, or to memory allocated on the stack. It is type-safe and memory-safe.

ReadOnlyUnmanagedSpan<T>.Enumerator

Enumerates the elements of a ReadOnlyUnmanagedSpan<T>.

UnmanagedSpan<T>

UnmanagedSpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed or native memory, or to memory allocated on the stack. It is type-safe and memory-safe.

UnmanagedSpan<T>.Enumerator

Enumerates the elements of a UnmanagedSpan<T>.

ValueCoordinatesIncrementor
ValueCoordinatesIncrementorAutoResetting
ValueOffsetIncrementor
ValueOffsetIncrementorAutoresetting

Delegates

TypelessConvertDelegate
ValueCoordinatesIncrementor.EndCallbackHandler