Skip to content

NumSharp 0.81.0 - NumPy 2.5.4 & TorchSharp

Latest

Choose a tag to compare

@github-actions github-actions released this 13 Nov 21:55
· 2 commits to master since this release
65ef305

This release moves NumSharp to NumPy 2.5.4, gives its functions NumPy's own signatures and brings NumPy's scalars, every Python and NumPy exception class, and 17 new np.* functions.
An NDArray now shares its memory with TorchSharp tensors, in both directions.

🧭 Overview

  • NumPy 2.x API coverage around 96.2%, everything except datetime, complex64 and other exotic dtypes is fully supported, with byte-to-byte equality and compatibility to NumPy.
    • NumPy's scalars - a 0-d result is NumPy's read-only scalar, an element read a[i, j] returns one, and NumPy's 36 scalar classes are callable (np.float64(1.5), np.int8("12")).
  • NumPy 2.5.4 is the reference (0.80.0: NumPy 2.4.2) - every oracle test case is NumPy 2.5.4's answer, with NumPy 2.5's behavior across linear algebra, random, numpy.ma, np.fft, text, sorting, reductions and dtypes, its keyword-only descending= sort, and NumPy 2.5.4's own OpenBLAS in NumSharp.Interop.OpenBLAS.
  • Fresh results laid out as NumPy's - ufunc results take NumPy's trivial-loop C / F or NpyIter K layout down to the strides of length-1 axes, keepdims reductions and np.median lay out as NumPy's, and every zero-size array has NumPy's all-zero strides.
  • NumSharp on phones - proven end to end on two Android phones with ONNX Runtime's CPU, XNNPACK and NNAPI providers: an XNNPACK session's outputs now import without a copy, and float-to-int casts on Mono run 4.5-5.7x faster with NumPy's bytes.
  • New documentation - the TorchSharp, Exceptions and NumPy Scalars pages, and a diagram of NumSharp's 18 released integrations opening the README and the Interoperability page.
  • 36 NumPy-parity, memory-safety and tooling fixes, 2 measured speedups and 20 breaking changes, across the release's 243 commits.
  • 1 new NuGet package - NumSharp.Interop.TorchSharp.
  • 1,122,248 NumPy 2.5.4 oracle test cases (0.80.0: 1,119,305 NumPy 2.4.2 cases) and 21,115 test methods (0.80.0: 20,506).

📦 New NuGet Packages

  • NumSharp.Interop.TorchSharp - zero-copy interop between NDArray and TorchSharp's tensors in both directions, with Auto / View / Copy modes.

Detailed Breakdown

Show All

📦 New NuGet Packages

One optional companion package ships for the first time, co-versioned with NumSharp (it depends on NumSharp). It converts arrays between NumSharp and TorchSharp, with no engine backend and no native asset of its own (it calls the libtorch that TorchSharp loads), so Core stays 100% managed without it.

  • NumSharp.Interop.TorchSharp - zero-copy interop between NDArray and TorchSharp's torch.Tensor (the .NET bindings for libtorch, the engine under PyTorch), in both directions and without Python.
    • Modes - every verb takes Auto (a view when the dtype and the layout allow it, else a copy; the default), View (shares memory or throws, naming what prevents the view) or Copy (always independent).
    • Export - AsTensor hands the array's buffer to libtorch: every non-negative-stride layout (contiguous, sliced, transposed, stepped) crosses as the same strided tensor, a broadcast of writeable memory as torch's own expand, and libtorch releases the buffer when the last tensor over it is freed; ToTensor copies.
    • Import - AsNDArray views a CPU tensor's storage through its own detach(), so the view survives set_ and the tensor's Dispose; ToNDArray copies; a tensor that requires grad, and an expanded one, import read-only (t.detach().AsNDArray() is the writeable route).
    • Dtypes - the ten dtypes both sides share cross as themselves; uint16 / uint32 / uint64 / char / decimal export by NumPy's NEP 50 promotion (int32 / int64 / float64 / int32 / float64), and an explicit dtype converts under NumPy's casting rule (same_kind by default); complex64 and bfloat16 tensors import widened exactly under Auto / Copy; a reversed view, a read-only array, a sparse, conjugated or non-CPU tensor copies under Auto; text arrays and quantized or meta tensors are refused in every mode.
    • Checks - MayShareMemory tells whether a result shares the source's memory, ToScalarType / ToDType map the dtypes, and live export / import counters help leak checks.
    • Proven - a state matrix holds 45 array states x the ten shared dtypes x the three modes to NumPy 2.5.4's flags and torch.from_numpy's layout, and 24 tensor states to Tensor.numpy(), with the deliberate differences asserted (read-only, misaligned and off-16-byte complex128 arrays copy where from_numpy shares).
    • Depends - TorchSharp 0.100.0 or later, the managed package only, with no upper bound (tested on 0.100.0 and 0.107.0); you reference the libtorch flavour you run (TorchSharp-cpu or a CUDA package), and on macOS install Homebrew's OpenMP first (brew install libomp), which libtorch links by Homebrew's path.

📊 Dashboards & Docs

  • Supported Features Dashboard - headline 96.2% (538 / 559 in NumPy 2.5.4's catalog), 368 with NumPy's exact signature and 539 oracle-verified; its coverage generator 1.18.0 counts the whole NumPy API at 70.3% (2,747 of 3,908; 0.80.0's generator read 41.6% of 4,844), because it credits what NumSharp provides and stops counting what NumPy itself refuses.
    • The ndarray operators and numpy.char / chararray are credited from the oracle cases that run them, NumPy's scalar types against NDArray (which now models them), and StringDType's members against DType.
    • Calls NumPy refuses (np.sin.reduce, del a[i]) and the members a class only inherits (memmap, recarray, MaskedRecords, record) show as "Not counted" instead of gaps.
  • Tests & Oracle Dashboard - 1,122,248 committed NumPy 2.5.4 oracle test cases over 1,455 operations (0.80.0: 1,119,305 NumPy 2.4.2 cases over 1,448), and 21,115 test declarations (0.80.0: 20,506).
  • Benchmark Dashboard - benchmark evidence for 441 of 520 benchmarkable APIs (0.80.0: 452 of 518).
    • np.acos, np.asin, np.atan, np.atan2, np.pow, np.bitwise_invert, np.bitwise_left_shift, np.bitwise_right_shift, np.cumulative_sum and np.cumulative_prod, which 0.80.0 credited through the functions they alias, are functions of their own now with no benchmark of their own, and np.fromiter, np.fromfunction and np.ndim have none yet.
  • Documentation site - new pages and sections, whose code samples run as tests:
    • TorchSharp - the new bridge, with what shares and what copies for 45 array states and 24 tensor states beside NumPy's and PyTorch's own answers.
    • Exceptions - every Python and NumPy class NumSharp raises, in one hierarchy with its .NET base, Python's attributes and the catch footguns (OverflowError is not OverflowException).
    • NumPy Scalars - the class calls, read-only scalars, and a scalar against a 0-d array.
    • The README and the Interoperability page open with a diagram of NumSharp's 18 released integrations.

✨ New APIs & Modules

  • NumPy's scalars - NumSharp keeps no scalar classes of its own: NumPy's scalar is a read-only 0-d NDArray, made at NumPy's own C-API points (PyArray_Return, PyArray_Scalar).
    • np.float64(1.5), np.int8("12"), np.complex128(1, 2), np.str_(5), np.bytes_(5) - the class calls of NumPy's 36 scalar class names, ported from NumPy's *_arrtype_new (C# 14; NDScalarClassCalls.float64(x) on C# 12): 3,042 of 3,210 NumPy calls answered identically, every other one understood.
    • A 0-d result is the read-only scalar NumPy returns - every ufunc and operator over 0-d operands, np.clip, np.round, np.dot / np.vdot / np.inner, np.sinc, np.nan_to_num, np.interp and the reductions; an out= array stays the array it was.
    • A full integer index (a[1, 2], a[0] of a 1-D array, z[()], m[0, 1]) reads NumPy's scalar, a read-only copy of the element; every key of a scalar indexes a copy of its value (s[...] is a writeable 0-d array, s[0] NumPy's "invalid index to scalar variable.").
  • Python's and NumPy's exceptions - every exception class of CPython 3.12 and NumPy 2.5.4 (47 new), each under a .NET twin and then Python's hierarchy, with Python's constructors, attributes and str() texts, raised where NumPy and CPython raise them.
    • ArithmeticError, OverflowError, ZeroDivisionError, FloatingPointError, MemoryError, ArrayMemoryError, EOFError, RecursionError, ReferenceError, NotImplementedError - the arithmetic, memory and runtime families, under ArithmeticException, OutOfMemoryException and EndOfStreamException where .NET has a twin.
    • OSError with errno / strerror / filename / winerror and its 15 subclasses (FileNotFoundError, PermissionError, IsADirectoryError, ...), chosen by errno through OSError.Create as Python's class call chooses them.
    • UFuncTypeError, UFuncNoLoopError, UFuncBinaryResolutionError, UFuncInputCastingError, UFuncOutputCastingError - NumPy's ufunc refusals, with ufunc, dtypes, casting, from_ and to.
    • SyntaxError / IndentationError / TabError (the location), NameError / UnboundLocalError / AttributeError (name, obj), UnicodeEncodeError / UnicodeDecodeError / UnicodeTranslateError (encoding, object, start, end, reason), StopIteration (value), SystemExit (code), KeyboardInterrupt, GeneratorExit, ImportError / ModuleNotFoundError, LapackError, ConverterError - Python's attributes.
    • BaseExceptionGroup / ExceptionGroup - Python's groups, with subgroup, split and derive.
    • Every class takes .NET's (message, innerException), Python's raise X(msg) from e, and a bare X() reads Python's str().
  • New functions and modules (17 new np.* functions; the ten most used on GitHub: np.round, np.shape, np.ndim, np.fromiter, np.astype, np.row_stack (kept, although NumPy 2.5 removed it), np.pow, np.fromfunction, np.atan and np.atan2) -
    • np.round - NumPy 2's name for rounding (np.round_ is gone).
    • np.acos, np.asin, np.atan, np.atan2, np.pow, np.bitwise_invert, np.bitwise_left_shift, np.bitwise_right_shift - NumPy 2.0's Array-API names.
    • np.cumulative_sum, np.cumulative_prod - NumPy 2.1's scans, with include_initial.
    • np.ndim, np.shape, np.astype, np.fromiter, np.fromfunction, np.row_stack and the constant np.little_endian.
    • np.lib.stride_tricks.sliding_window_view and np.lib.stride_tricks.as_strided (NumPy's byte strides, kept inside the array's buffer).
    • np.lib.array_utils (byte_bounds, normalize_axis_index, normalize_axis_tuple) and np.lib.format (read_magic, read_array_header_1_0 / 2_0, write_array_header_1_0 / 2_0, read_array, write_array, header_data_from_array_1_0, dtype_to_descr, descr_to_dtype, drop_metadata).
    • np.ma.MaskedArray(...), new np.NDIndex(2, 3), new np.NDIterator(...), new np.Broadcast(...) and new NDCharArray(...) - NumPy's class calls, with NumPy's defaults.
  • descending= - NumPy 2.5's keyword-only descending sort on np.sort, np.argsort, ndarray.sort and ndarray.argsort: NaN still sorts last, ties land where NumPy's default introsort or its stable timsort / radix sort puts them, and a StringDType array, a masked array or a kind= beside it is refused as NumPy refuses it.
  • NumPy's one signature - one overload in NumPy's parameter order, with NumPy's names, defaults and Nones, over thirteen validated rounds (each pick compiled before and after at C# 12, with NumPy 2.4.2's answers as its tests): 1,047 NumPy names now take NumPy's exact signature (0.80.0: 719).
    • NDAxis - NumPy's axis argument (None, an int or a tuple) as one type, so np.sum(a, axis: (0, 2)) is one reduction over the set with NumPy's bits; taken by sum, prod, mean, var, std, max / amax, min / amin, the seven nan reductions, all, any, count_nonzero, ptp, median / percentile / quantile and their nan forms, average, flip, squeeze, expand_dims, moveaxis, roll, trim_zeros, size, gradient, fft.fftshift, linalg.norm / vector_norm and apply_over_axes, with the int and int[] forms kept beside it.
    • None wherever NumPy takes it - axis: null (it was ambiguous for nine functions), size: null for every np.random sampler, order, density, and the shape of the *_like family.
    • Creation - np.linspace and np.geomspace take array_like ends (float32 ends compute in float32, axis=, and retstep= for linspace), np.arange(start, stop, step, dtype, device) takes any array_like, np.zeros / ones / empty / full take order and device, np.reshape NumPy's copy modes, np.asanyarray copy, and Shape is a C# collection (np.zeros([2, 3]), foreach (long d in a.Shape)).
    • Keywords - np.stack (out, dtype, casting), np.hstack / np.vstack (dtype, casting), np.nansum / np.nanprod (initial, where), np.nanmean / np.nanvar / np.nanstd (where, mean, correction), np.ldexp (dtype), np.frexp / np.modf (out=(o1, o2)), np.tensordot (axes=(a, b)), np.require (any iterable of requirements), and np.interp's fill values converted as NumPy converts them.
    • Result structs - np.intersect1d(return_indices), np.average(returned), np.indices(sparse) and np.linspace(retstep) return one value that converts to the array and deconstructs into NumPy's tuple.
    • numpy.ma - the 48 masked ufuncs take out / where / dtype, the reductions tuple axes and NumPy's out=, ma.sort / ma.argsort take kind / order / stable, ma.median out / overwrite_input, ma.array / masked_array NumPy's argument order, and MaskedArray gains base, device, to_device, getfield, setfield and item(*args).
    • finfo - nmant, iexp, nexp, machep and negep; np.iinfo / np.finfo read dtype strings as NumPy does.
  • The bit generators' State snapshots (MT19937, PCG64, PCG64DXSM, Philox, SFC64) and poly1d are unsealed: a subclass engine carries its own state, and a poly1d subclass's operations return a plain poly1d, as NumPy's do.

🧩 ndarray surface

  • astype(dtype, order, casting, copy), reshape(shape, order, copy) (and loose integers), transpose(*axes), dot(b, out) and the reductions' tuple axes - NumPy's method signatures on NDArray, NDArray<T>, NDMatrix, NDCharArray and NDMaskedArray.

⚡ Performance

Ratios are NumPy ÷ NumSharp - higher is better (x2 = twice NumPy's speed); xLOW->xHIGH spans the worst→best measured cell across sizes and dtypes.

  • x4.0->x6.7 - np.argpartition / np.partition with one kth near either end of a long contiguous line (a top-k) find it in one read: a 51,200-wide float32 row's top 5 and 1M elements (the argpartition was x0.44).
  • x1.6->x2.0 - the ONNX Runtime and ML.NET bridges' Postprocess.TopK selects with np.argpartition (and np.argmax for k = 1) instead of sorting the whole row, measured against NumPy's argpartition idiom over a 51,200-wide row (it was x0.12).

🎯 Parity & Fixes

  • NumPy 2.5's behavior, as NumPy 2.5.4 answers -
    • Linear algebra - np.linalg.eig / eigvals always return complex, svd / lstsq refuse inf or NaN with LinAlgError before LAPACK runs, cond reads a failed SVD as NaN and p='nuc' sums the singular values, svd(hermitian=True) signs a zero eigenvalue +1, and tensorsolve returns a view.
    • Random - Generator's binomial, negative_binomial and multinomial draw through NumPy 2.5's corrected BTPE (RandomState keeps the legacy stream), SeedSequence refuses nested sequences and spawn a negative count, np.random.seed over a swapped bit generator drops the cached Gaussian, and choice / multivariate_normal return views.
    • numpy.ma - a stored fill_value no longer follows a dtype change (converted with its errors caught, else dropped), mask_or passes shrink on, and the shared-mask resize text is NumPy 2.5's.
    • np.fft - ifft2, irfft2 and hfft write the caller's out= (it was checked, then ignored), and the forward transforms' invalid-norm text is NumPy 2.5's.
    • Text - StringDType compares every byte (trailing NULs are characters), a loop takes the effective NA descriptor, products over text resolve a common descriptor, bytes cast to StringDType are checked as UTF-8, and multiply / replace / zfill / slicing follow NumPy 2.5's count and overflow rules.
    • Sorting and searching - np.searchsorted runs NumPy 2.5's batched bisection and checks a sorter's middle entry even with no keys, and np.unique and the set routines keep each NaN's own bits.
    • Reductions - np.cumsum / np.cumprod refuse an out= whose dimensions differ from the input's, and the quantile family answers several reduced axes beside a kept axis of length 0 (an empty result, not a reshape error).
    • Dtypes - np.finfo(null) raises TypeError and a non-float dtype names NumPy 2.5's text, np.isdtype accepts a StringDType descriptor, a StringDType sub-array ('2T') is NumPy's TypeError, the 'a' alias is gone, and np.sctypeDict / np.typecodes raise KeyError for a missing key.
    • Core - np.where makes a Python-literal operand in the common dtype (an out-of-range literal is OverflowError; it wrapped), take / compress check out= in NumPy 2.5's direction, float16 nextafter of equal zeros returns y, and ndarray.resize's refusal is NumPy 2.5's wording.
  • np.partition / np.argpartition with several kth, and the quantile family, run NumPy's introselect_ statement for statement: equal values land where NumPy puts them (the bytes, the sign of a ±0 tie in np.percentile / np.quantile, argpartition's tied indices, which NaN a NaN slice answers), with a single kth and the speed kept; a zero np.median is +0.0 and a complex median follows np.mean's statements (np.median([1+infj]) is nan+infj).
  • float32 / float64 np.prod multiplies in NumPy's sequential multiply.reduce order on every route: NumPy's last bits, and an overflow before a zero is NaN (it was 0).
  • A fresh ufunc result takes NumPy's trivial-loop C / F allocation or NpyIter's K order, down to the strides of length-1 axes; a keepdims reduction keeps its axis at NumPy's stride, np.median is laid out as NumPy's mean of the partitioned copy (keepdims a stride-0 view), and every zero-size allocation has all-zero strides.
  • np.ldexp of a contiguous view at an offset (an np.split child) reads the view (it scaled the owner's first elements), and np.positive of a read-only array is writeable.
  • np.unwrap(p, 4) reads 4 as discont, NumPy's second parameter (it was the period-4 unwrap, a silent wrong answer).
  • np.empty_like(a, null, 'F') is NumPy's F-ordered array of a's shape (the order letter bound the Shape parameter and returned a (70,) array).
  • np.zeros(t.Shape), np.ones, np.empty and np.full of a transposed view's shape are C-ordered, as NumPy's np.zeros(t.shape) is (they were F).
  • np.arange with C# float bounds is float64, as NumPy's Python floats give (it computed in float32), and a C# ulong bound is a Python int (np.arange(18446744073709551610UL, 18446744073709551615UL, 2UL) was an empty float32 array).
  • np.linspace keeps integer ends past 2^24 exact (np.linspace(-20000001, 20000001, 3); they were rounded to float32).
  • np.asanyarray(300, np.int8) raises NumPy's OverflowError (it wrapped to 44), and np.asanyarray(5, np.str_) is <U1.
  • np.finfo(float32 / float64).minexp is NumPy's -126 / -1022 (it was C's -125 / -1021), and float32's smallest_normal / tiny are exactly 2^-126.
  • item() of a non-size-1 array and item(i, j) with the wrong number of indices raise NumPy's ValueError and texts, and np.repeat's negative-count text ends with NumPy's period.
  • np.array(["SFFF", "FHFH", "FFFH", "HFFG"], "c") splits each text into its characters, NumPy's character dtype (Gymnasium's maps), and keeps the 'c' type.
  • np.subtract of two bool arrays with dtype=float64 or int8 computes as NumPy's (only the bool loop refuses).
  • np.all / np.any of a numpy.matrix along an axis return NumPy's matrix (they returned plain arrays).
  • np.unravel_index / np.ravel_multi_index take shapes past int32 (a (2**40, 3) shape unravels).
  • numpy.ma -
    • np.ma.clip with a masked bound masks those elements (the bound's mask was lost).
    • np.ma.make_mask(m, False) / np.ma.mask_or(m1, m2, False) read False as NumPy's copy (it was shrink).
    • np.ma.choose reads the choices filled with their own fill values (it read the raw data), and MaskedArray.choose is ndarray.choose (it ran the module form).
    • An F-ordered astype / *_like result gets an F-ordered mask (it was C).
  • Smaller alignments -
    • np.isscalar(np.array("x")) is False, as for any array np.array builds.
    • np.concatenate reports a later 0-d array as NumPy's rank mismatch.
    • np.fft.fftshift / ifftshift of a 0-d array raise NumPy's error (they returned a copy).
  • NumSharp.Interop.OpenBLAS - bundles OpenBLAS 0.3.34.106.0, the build NumPy 2.5.4 ships (byte-identical to its library), so the products and LAPACK factorizations give NumPy 2.5.4's bits; the build-time version override now finds glibc Linux wheels by platform family, so it can move between manylinux2014 and manylinux_2_27 / 2_28 releases (it found no wheel).
  • NumSharp.Build - deconstructing a result struct (np.meshgrid, np.unique, np.gradient, the histograms, ...) after a deconstruction that held matrices returned the previous statement's arrays; the weaver no longer touches a Deconstruct.
  • NumSharp.Interop.OnnxRuntime - the outputs of an XNNPACK session (ONNX Runtime's Android / iOS CPU accelerator) import without a copy (they were refused as device memory), proven end to end for the CPU, XNNPACK and NNAPI providers on two Android phones, and the synchronous Run's own overhead drops 30% per step of a 27-input decoder.
  • Float to int8 / uint8 / int16 / uint16 / int32 casts on hosts without the x86 vector cast (Mono's .NET for Android, iOS and WebAssembly) no longer call Math.Truncate per element, which Mono compiles to a call: 4.5-5.7x faster on an arm64 Android phone, with NumPy's bytes for all 2^32 float32 inputs.

🧰 Testing & Tooling

  • NumPy 2.5.4 is the oracle's NumPy, written once in numpy-version.txt (the generators, the coverage generator, the workflows, the interop environments and the C# corpus gates read it), and every corpus and fixture is regenerated under it: the cases NumPy 2.5 fixed return (StringDType byteswap / place, raw text cell copies in choose / put / putmask, invalid UTF-8, tofile of non-ASCII text), and the sort tier gains descending=True.
  • A second NumPy parity QA journey - about 372,000 probe comparisons over 160 log rows (random fuzzers, an ambiguity fuzzer compiling NumPy's call spellings at C# 12 and 14, NaN-identity, aliasing and layout sweeps): 62 findings, 58 of them new, each an [OpenBugs] reproduction with a journal entry; 9 are fixed in this release.
  • Every answer of the 0.80.0 issue triage (34 issues closed, 4 progress comments) is a test: 35 Issue<n> classes assert each answer's claims against NumPy 2.4.2, and each gap an answer admits is an [OpenBugs] pin.
  • The np.* signature gap map is validated against its ground truth (the C# compiler for missing keywords, NumPy's runtime for refusals, tuples and defaults; 19 findings refuted), and docs/plans/API_PARITY_DIFF.md lists each change as a patch: the list the thirteen rounds were picked from.
  • Oracle tiers - cast_boundary (float32 / float64 -> int8 ... int32 at every integer edge, NaN payloads and infinities, zero excuses; all 2^32 float32 inputs proven once against NumPy), the layout-parity ufunc family (263 cases), np.linspace over array ends, and the surface tier's cumulative_sum / cumulative_prod, fromiter, fromfunction and sliding_window_view.
  • Releases pause before nuget.org until the owner approves the deployment: the publish job runs in the GitHub environment nuget, and the nuget.org Trusted Publishing policy accepts only that environment.

💥 Breaking Changes

  • NumPy's parameter names (calls that name the old ones must be renamed): np.transpose(a, axes) (was premute), np.array_equal(a1, a2), np.iscomplex / isreal / iscomplexobj / isrealobj(x), np.isscalar(element), np.array_repr(arr), np.cumsum / np.cumprod(a, axis, dtype, out) (were arr / typeCode), reshape(shape) (was newShape), np.unravel_index(indices, shape), np.ravel_multi_index(multi_index, dims, mode), np.insert(arr, obj, values), np.tensordot(a, b, axes: (a, b)) (were two lists), np.ma.default_fill_value(obj), np.ma.size(obj) and MaskedArray.clip(min, max).
  • Removed names: np.round_ (use np.round), np.average_returned / np.ma.average_returned (returned: true), np.indices_sparse (sparse: true), the generic np.zeros<T> / ones<T> / empty<T> / full<T> / argsort<T> / finfo<T> / iinfo<T> / can_cast<TFrom, TTo> (pass the dtype), NDArray.Normalize() and AxisOutOfRangeException.
  • Calls that no longer compile: np.zeros(shape, 'F') (name order: 'F'; NumPy reads that letter as a dtype), np.tile(a, 2, 2) (np.tile(a, (2, 2))), np.random.random_sample(2, 3) / rand(Shape) (random_sample((2, 3)) / rand(2, 3)), astype(t, false) (astype(t, copy: false)), np.frexp(x, m, e) (out: (m, e)), np.ma.array(data, mask) (name mask:), np.ma.reshape(a, 2, 3) (a tuple), np.fft.fftfreq with a float n, dtype.newbyteorder('>') (">"), an object-typed value to np.insert / np.append, and a method group of a unary ufunc (Select(np.exp) needs a lambda).
  • Positional arguments that now land on NumPy's parameter: the fourth argument of np.average / np.ma.average is returned (it was keepdims, so name keepdims:; a silent change), the second argument of np.unwrap is discont (it was the period; silent too), and a keepdims passed after the axis of np.all / np.any / np.argmax / np.argmin lands in out and raises NumPy's TypeError.
  • Refusals that follow NumPy: np.can_cast refuses a Python number with NumPy's NEP 50 TypeError (pass an array or a dtype), np.isdtype / np.issubdtype / np.iinfo / np.finfo refuse arrays (pass a.dtype), np.require raises KeyError for an unknown requirement (it was ValueError), and np.ma.amax / np.ma.amin take no fill_value.
  • NpzFile has NumPy's names: files, f, zip, fid, allow_pickle, max_header_size, keys(), values(), items(), get() and close() (Files, F, Zip, AllowPickle, MaxHeaderSize and Close() are gone).
  • Engine and subclass hooks: the TensorEngine reductions (ReduceAdd ... NanStd) and CountNonZero take NDAxis?, NDArray._collapse(result, NDAxis?) replaces the int? hook, TensorEngine.Geomspace / Interp take arrays, and UnravelIndex / RavelMultiIndex take long[] dims.
  • NDArray.Scalar(...) returns NumPy's read-only scalar (a write raises ValueError); build a mutable 0-d buffer with np.array(x) or np.zeros(Shape.Scalar, dtype).
  • A 0-d result of a ufunc, an operator, np.clip, np.round, np.dot and the other return points is a read-only scalar, and np.isscalar(np.array(5)) is False.
  • An element read (a[i, j], a[i] of a 1-D array) is a read-only copy, not a writeable 0-d view: write through the setter (a[i, j] = v); a read costs ~0.55 us (the view 0.36 us), so a hot loop keeps GetValue<T> / GetDouble.
  • NumPy's errors are Python's classes: catch (OverflowException) / (DivideByZeroException) / (FileNotFoundException) / (KeyNotFoundException) no longer see them (catch OverflowError, ZeroDivisionError, FileNotFoundError, KeyError, or the .NET bases ArithmeticException / IOException), and an exact-type check for TypeError no longer matches a ufunc refusal (a UFuncTypeError).
  • A bare Python-named exception's Message is Python's str() (""; it was the class name or .NET's default text); numpy.ma's tofile / compress_rows / compress_cols / compress_rowcols / mask_rowcols, notmasked_contiguous over more than 2-D and a seedless SeedSequence raise NumPy's NotImplementedError, a RuntimeError (they raised NotImplementedException / NotSupportedException); and item() / MaskedArray.resize raise ValueError (they raised IncorrectSizeException / NotSupportedException).
  • .strides of fresh results follow NumPy's: length-1 axes of trivial-loop results and of keepdims reductions, every zero-size allocation (np.zeros((2, 0)).strides is (0, 0)), np.median(..., keepdims: true) (a stride-0 view, OWNDATA false), and np.zeros / ones / empty / full, which lay out from the dimensions alone (an F Shape gives C; pass order: 'F').
  • float32 / float64 np.prod multiplies sequentially, as NumPy does: the last bits change, an overflow before a zero is NaN, and a flat product runs at NumPy's speed (the SIMD lanes ran it 6-16x faster).
  • np.partition / np.argpartition with several kth arrange equal values as NumPy's introselect does, so the bytes around tied values change.
  • np.arange with C# float bounds returns float64 (np.arange(0f, 1f, 0.1f) was float32).
  • np.append of a C# number appends NumPy's array of the Python number: np.append(int32_array, 5) is int64 and np.append(float32_array, 1.5f) float64 (they kept the array's dtype).
  • NumPy 2.5's answers: np.linalg.eig / eigvals return complex even when every eigenvalue is real (take .real), a seeded Generator's binomial, negative_binomial and multinomial draw NumPy 2.5's corrected stream (np.random.* and RandomState keep the legacy one), and np.unique / the set routines keep a NaN's own bits (they returned the canonical NaN).
  • NumPy 2.5's refusals: np.cross / np.linalg.cross of 2-element vectors (give them a zero third component), np.cumsum / np.cumprod into an out= whose dimensions differ from the input's, the 'a' dtype alias (np.dtype("a5"); write "S5"), and np.finfo(null) (a null gave float64's limits).
  • NumPy 2.5's text rules: a StringDType text's trailing NULs are characters (its length, the comparisons and strip see them), byteswap of a StringDType array raises NumPy's TypeError (it copied), a view or getfield through a separate StringDType() descriptor raises, and bytes that are not UTF-8 no longer cast to StringDType.

Install via NuGet

dotnet add package NumSharp --version 0.81.0
dotnet add package NumSharp.Bitmap --version 0.81.0
dotnet add package NumSharp.Interop.MLNet --version 0.81.0
dotnet add package NumSharp.Interop.OnnxRuntime --version 0.81.0
dotnet add package NumSharp.Interop.OpenBLAS --version 0.81.0
dotnet add package NumSharp.Interop.System.Numerics.Tensors --version 0.81.0
dotnet add package NumSharp.Interop.TorchSharp --version 0.81.0
dotnet add package NumSharp.Interop.pythonnet --version 0.81.0
dotnet add package NumSharp.Build --version 0.81.0

Packages

Package NuGet
NumSharp NuGet
NumSharp.Bitmap NuGet
NumSharp.Interop.MLNet NuGet
NumSharp.Interop.OnnxRuntime NuGet
NumSharp.Interop.OpenBLAS NuGet
NumSharp.Interop.System.Numerics.Tensors NuGet
NumSharp.Interop.TorchSharp NuGet
NumSharp.Interop.pythonnet NuGet
NumSharp.Build NuGet