Table of Contents

Interface ISlidingDotBackend

Namespace
NumSharp.Backends
Assembly
NumSharp.dll

An optional capability a IBlasBackend MAY also implement to supply the byte-parity level-1 dot product (NumPy's per-dtype dotfunc) behind the sliding multiply-accumulate family — np.correlate and np.convolve.

public interface ISlidingDotBackend

Remarks

Why a second, separate interface. IBlasBackend is the matrix-product seam: every member there takes whole NDArray operands. correlate/ convolve are not matrix products — they are a sliding dot whose inner reduction runs once per OUTPUT position (thousands of tiny dots), so the primitive they need is a raw strided vector dot called in a hot loop, not an allocating whole-array method. Rather than put a pointer method on the product interface, this capability is discovered by a cast: engine.Blas as ISlidingDotBackend. A backend that does not implement it simply leaves the managed sliding kernels in place — the exact optional-capability pattern TryMatMulBatched(NDArray, NDArray, NDArray) expresses as a default method.

What it is byte-for-byte. This is NumPy's @name@_dot from numpy/_core/src/multiarray/arraytypes.c.src — the very dotfunc that _pyarray_correlate (multiarraymodule.c) calls for every ramp position and for the middle whenever small_correlate declines. NumPy routes only float32, float64 and complex128 through cblas here (its #USEBLAS = 1,1,0,0,1,1,… over FLOAT/DOUBLE/…/CFLOAT/CDOUBLE), summing each dot in a double accumulator "for stability" (a chunked ?dot, ?dotu for complex). A backend supplies exactly that so np.correlate/np.convolve match NumPy to the last bit on the long float kernels a portable reduction reorders — the same reason NumSharp.Interop.OpenBLAS exists for the matrix products.

Which positions the engine routes here. The engine keeps the whole small_correlate-eligible regime — real float32/float64 kernels of length ≤ 11 — on its own managed kernel, which is already byte-identical to NumPy there (a plain sequential sum). Only real kernels longer than 11 and every complex kernel reach this interface, and for those NumPy sends every ramp AND middle position through cblas, so the engine does too — no mixed managed/native path within one call.

Reading the operands. Element strides, not bytes (NumPy's are bytes — the same logic with itemsize == 1). The operands the engine hands over are always contiguous, offset-0 buffers of the result dtype, so the stride is one element; the pointers advance to the sub-array a given output position dots. Byte-parity still depends on the same three levers the products do (the OpenBLAS build, its thread count, the dispatched DYNAMIC_ARCH kernel) — level-1 ?dot is typically single-threaded, but a host whose CBLAS differs is free to produce different bits, exactly as with dot/matmul.

Methods

Dot(NPTypeCode, void*, long, void*, long, void*, long)

Computes one strided vector dot, NumPy's @name@_dot: *result = Σ a[i·strideA] · b[i·strideB] for i in [0, count), summed the way NumPy sums it (chunked cblas ?dot accumulated in double; ?dotu — UNCONJUGATED — for complex, matching np.correlate/np.convolve, which never conjugate the kernel here because correlate has already conjugated it).

void Dot(NPTypeCode dtype, void* a, long strideA, void* b, long strideB, void* result, long count)

Parameters

dtype NPTypeCode

One of Single/Double/Complex — the caller has checked SupportsDot(NPTypeCode) first.

a void*

Base pointer of the first operand's sub-array.

strideA long

Element stride of a (one, for a contiguous buffer).

b void*

Base pointer of the second operand's sub-array.

strideB long

Element stride of b.

result void*

Where the scalar dot is written (one element of dtype).

count long

Number of terms in the dot (may be 0, which writes the zero sum).

DotBatch(NPTypeCode, void*, void*, void*, long, long)

The uniform, fully-overlapping MIDDLE region of a sliding correlate, computed in ONE call — the hot loop np.correlate/np.convolve spend nearly all their time in. For i in [0, count): result[i] = Σ_{t=0}^{n2-1} a[i + t] · b[t] (contiguous, element stride 1) — i.e. count dots each of length n2 over the sliding window of a, each summed exactly like Dot(NPTypeCode, void*, long, void*, long, void*, long) (NumPy's @name@_dot). This is the SAME per-position dot NumPy's _pyarray_correlate runs across the fully-overlapping positions; hoisting it behind one interface call is what lets the virtual-dispatch + dtype-switch cost be paid ONCE for the whole region instead of once per output. For a 100K signal that is a ~100K-fold reduction in dispatch — measured to remove ~1.45 ms of pure per-position overhead from a 2.4 ms call, leaving only the native ?dot cost that already matches NumPy.

The default implementation just calls Dot(NPTypeCode, void*, long, void*, long, void*, long) per position (so a backend that does not override it keeps the exact old behaviour and byte-parity); a backend overrides it to run the tight loop against its native primitive directly, with no per-position interface dispatch. The result is bit-identical either way — only the dispatch is hoisted.

void DotBatch(NPTypeCode dtype, void* a, void* b, void* result, long count, long n2)

Parameters

dtype NPTypeCode

One of Single/Double/Complex — the caller has checked SupportsDot(NPTypeCode) first.

a void*

Base pointer of the data array; the i-th dot starts at a[i].

b void*

Base pointer of the kernel (the same for every position), length n2.

result void*

Base pointer of the contiguous output; the i-th dot is written to result[i].

count long

Number of output positions (fully-overlapping middle length).

n2 long

Kernel length — the term count of each dot.

SupportsDot(NPTypeCode)

Whether this backend supplies the byte-parity dot for dtype. NumPy routes only float32/float64/complex128 through cblas here; complex is served only when the loaded library also exports the complex products. Every other dtype returns false and the engine keeps its managed kernel — which is bit-exact by construction for the integer/bool families and matches NumPy's own scalar loops for the rest.

bool SupportsDot(NPTypeCode dtype)

Parameters

dtype NPTypeCode

Returns

bool