Namespace NumSharp.Utilities
Classes
- ArrayConvert
Presents all possible combinations of array conversion of types supported by numpy.
- 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
Tthat 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 innpy_math_complex.c.src(the FreeBSD msun implementations) becauseSystem.Numerics.Complexdiverges 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|=1log1ppath; drives Log10(Complex) and the engine'slog2); Sinh(Complex)/ Cosh(Complex) = textbooksinh/cosh(x)·trig(y)with ay==0guard (so a huge real part doesn't becomeinf·0 = NaN) and the C99 Annex G non-finite tables; Tanh(Complex) = Kahan'snpy_ctanh(markedly more accurate than the BCL near±π/2) + the|x|≥22overflow-safe branch; Sin(Complex)/Cos(Complex)/ Tan(Complex) route through those exactly as NumPy definescsin/ccos/ctan; Atan(Complex) = the fullnpy_catanh(realatanh/atanon the axes, thelog1pinterior, and an exponent-classifiedreal_part_reciprocal); Exp(Complex) =npy_cexp; Sqrt(Complex) =npy_csqrt; Expm1(Complex) =nc_expm1with a Goldberg realexpm1; Square(Complex) = FMA-contractedz·z(matches NumPy's complex multiply overflow/cancellation); Reciprocal(Complex) = Smith'snc_recip; Exp2(Complex)/Log1p(Complex) compose the above; Abs(Complex) =npy_cabs(C99hypot: an infinite component yields+infeven alongside a NaN — the .NET 8Complex.Absreturns 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/sinwith a NaN imaginary part pick the C99-unspecified sign for the resulting zero;arccoswith a sub-DBL_MINimaginary part flushes the denormal real part to 0 where NumPy'scacoshard-work kernel keeps it (~5.8e-309);sinh/coshat the|x|∈[710,710.13]overflow edge differ because Windows' CRTsinhoverflows 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.
- NDDivision
floor-division and remainder helpers matching NumPy's
floor_div_@TYPE@(loops_arithmetic.dispatch.c.src), integerremainder(loops_modulo.dispatch.c.src), and the floating-pointnpy_floor_divide@c@/npy_remainder@c@(Python-divmod port innpy_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 // -1wraps toMIN(overflow), matching NumPy'snpy_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), soa // 0.0is±inf/nan(not forced NaN) and edge cases like0.7 // 0.1 == 6.0and-2.0 // inf == -1.0match.
- Integer divide/modulo by zero returns
- NDIntegerPower
Integer power helpers matching NumPy's
@TYPE@_powerloop inloops.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 (seeDefaultEngine.Power).
- NonGenericConvert
Provides a way to convert boxed object from known time to specific type.
- 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
- 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>.