# Dense Distances

> NumKong for C

The dense spatial kernels cover the SciPy-style `sqeuclidean`, `euclidean`, and `angular` family.
The important difference is that storage type and output type are not forced to match.

```c
nk_f16_t a[768];
nk_f16_t b[768];
nk_f32_t sqeuclidean = 0, euclidean = 0, angular = 0;

// `_Float16` support varies across compilers, and
// auto-vectorization targets `f32` — not `f16`.
nk_sqeuclidean_f16_best(a, b, 768, &sqeuclidean, capabilities, NULL);
nk_euclidean_f16_best(a, b, 768, &euclidean, capabilities, NULL);
nk_angular_f16_best(a, b, 768, &angular, capabilities, NULL);
```

For `i8`, `u8`, `i4`, `u4`, and `u1`, the widening is even more important.
The output type is chosen to avoid the obvious overflow trap of same-width accumulation.

`#include <numkong/spatial.h>`

## nk_euclidean_*_best

L2 (Euclidean) distance between two vectors.

```c
nk_status_t nk_euclidean_f64_best(
    nk_f64_t const *a,
    nk_f64_t const *b,
    nk_size_t n,
    nk_f64_t *result,
    nk_capability_t capabilities,
    nk_stream_t stream);
```

- `nk_euclidean_f64_best`: a, b `nk_f64_t`, result `nk_f64_t`
- `nk_euclidean_f32_best`: a, b `nk_f32_t`, result `nk_f64_t`
- `nk_euclidean_f16_best`: a, b `nk_f16_t`, result `nk_f32_t`
- `nk_euclidean_bf16_best`: a, b `nk_bf16_t`, result `nk_f32_t`
- `nk_euclidean_e4m3_best`: a, b `nk_e4m3_t`, result `nk_f32_t`
- `nk_euclidean_e5m2_best`: a, b `nk_e5m2_t`, result `nk_f32_t`
- `nk_euclidean_e2m3_best`: a, b `nk_e2m3_t`, result `nk_f32_t`
- `nk_euclidean_e3m2_best`: a, b `nk_e3m2_t`, result `nk_f32_t`
- `nk_euclidean_i8_best`: a, b `nk_i8_t`, result `nk_f32_t`
- `nk_euclidean_u8_best`: a, b `nk_u8_t`, result `nk_f32_t`
- `nk_euclidean_i4_best`: a, b `nk_i4x2_t`, result `nk_f32_t`
- `nk_euclidean_u4_best`: a, b `nk_u4x2_t`, result `nk_f32_t`

- `nk_euclidean_f64` backends: serial, neon, sve, haswell, skylake, v128relaxed, rvv, powervsx, loongsonasx
- `nk_euclidean_f32` backends: serial, neon, sve, haswell, skylake, v128relaxed, rvv, powervsx, loongsonasx
- `nk_euclidean_f16` backends: serial, neon, svehalf, haswell, skylake, diamond, v128relaxed, rvv, rvvhalf, powervsx, loongsonasx
- `nk_euclidean_bf16` backends: serial, neon, neonbfdot, svebfdot, haswell, genoa, v128, v128relaxed, rvv, rvvbf16, powervsx, loongsonasx
- `nk_euclidean_e4m3` backends: serial, neon, neonfp8, haswell, skylake, icelake, diamond, v128relaxed, rvv
- `nk_euclidean_e5m2` backends: serial, neon, neonfp8, haswell, skylake, diamond, v128relaxed, rvv
- `nk_euclidean_e2m3` backends: serial, neon, neonfp8, haswell, skylake, icelake, sierra, alder, v128relaxed
- `nk_euclidean_e3m2` backends: serial, neon, neonfp8, haswell, skylake, icelake, sierra, alder, v128relaxed
- `nk_euclidean_i8` backends: serial, neonsdot, svesdot, haswell, icelake, sierra, alder, v128, v128relaxed, rvv, powervsx, loongsonasx
- `nk_euclidean_u8` backends: serial, neonsdot, svesdot, haswell, icelake, sierra, alder, v128, v128relaxed, rvv, powervsx, loongsonasx
- `nk_euclidean_i4` backends: serial, neonsdot, icelake, rvv
- `nk_euclidean_u4` backends: serial, neonsdot, icelake, rvv

- `a` (in): The first vector.
- `b` (in): The second vector.
- `n` (in): Counts dimensions, a multiple of the values per byte.
- `result` (out): The output distance value.
- `capabilities` (in): One device's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports.
- `stream` (in): Null on the CPU, or the GPU stream of that device to queue on.

Notes: The output distance value is non-negative.

Notes: The output distance value is zero if and only if the two vectors are identical.

Statuses: - [`nk_missing_kernel_k`](https://www.unum.cloud/docs/numkong/c/quickstart.md#nk_missing_kernel_k) No capability in `capabilities` has it.

## nk_sqeuclidean_*_best

Squared L2 (Euclidean) distance between two vectors.

```c
nk_status_t nk_sqeuclidean_f64_best(
    nk_f64_t const *a,
    nk_f64_t const *b,
    nk_size_t n,
    nk_f64_t *result,
    nk_capability_t capabilities,
    nk_stream_t stream);
```

- `nk_sqeuclidean_f64_best`: a, b `nk_f64_t`, result `nk_f64_t`
- `nk_sqeuclidean_f32_best`: a, b `nk_f32_t`, result `nk_f64_t`
- `nk_sqeuclidean_f16_best`: a, b `nk_f16_t`, result `nk_f32_t`
- `nk_sqeuclidean_bf16_best`: a, b `nk_bf16_t`, result `nk_f32_t`
- `nk_sqeuclidean_e4m3_best`: a, b `nk_e4m3_t`, result `nk_f32_t`
- `nk_sqeuclidean_e5m2_best`: a, b `nk_e5m2_t`, result `nk_f32_t`
- `nk_sqeuclidean_e2m3_best`: a, b `nk_e2m3_t`, result `nk_f32_t`
- `nk_sqeuclidean_e3m2_best`: a, b `nk_e3m2_t`, result `nk_f32_t`
- `nk_sqeuclidean_i8_best`: a, b `nk_i8_t`, result `nk_u32_t`
- `nk_sqeuclidean_u8_best`: a, b `nk_u8_t`, result `nk_u32_t`
- `nk_sqeuclidean_i4_best`: a, b `nk_i4x2_t`, result `nk_u32_t`
- `nk_sqeuclidean_u4_best`: a, b `nk_u4x2_t`, result `nk_u32_t`

- `nk_sqeuclidean_f64` backends: serial, neon, sve, haswell, skylake, v128relaxed, rvv, powervsx, loongsonasx
- `nk_sqeuclidean_f32` backends: serial, neon, sve, haswell, skylake, v128relaxed, rvv, powervsx, loongsonasx
- `nk_sqeuclidean_f16` backends: serial, neon, svehalf, haswell, skylake, diamond, v128relaxed, rvv, rvvhalf, powervsx, loongsonasx
- `nk_sqeuclidean_bf16` backends: serial, neon, neonbfdot, svebfdot, haswell, genoa, v128, v128relaxed, rvv, rvvbf16, powervsx, loongsonasx
- `nk_sqeuclidean_e4m3` backends: serial, neon, neonfp8, haswell, skylake, icelake, diamond, v128relaxed, rvv
- `nk_sqeuclidean_e5m2` backends: serial, neon, neonfp8, haswell, skylake, diamond, v128relaxed, rvv
- `nk_sqeuclidean_e2m3` backends: serial, neon, neonfp8, haswell, skylake, icelake, sierra, alder, v128relaxed
- `nk_sqeuclidean_e3m2` backends: serial, neon, neonfp8, haswell, skylake, icelake, sierra, alder, v128relaxed
- `nk_sqeuclidean_i8` backends: serial, neonsdot, svesdot, haswell, icelake, sierra, alder, v128, v128relaxed, rvv, powervsx, loongsonasx
- `nk_sqeuclidean_u8` backends: serial, neonsdot, svesdot, haswell, icelake, sierra, alder, v128, v128relaxed, rvv, powervsx, loongsonasx
- `nk_sqeuclidean_i4` backends: serial, neonsdot, icelake, rvv
- `nk_sqeuclidean_u4` backends: serial, neonsdot, icelake, rvv

- `a` (in): The first vector.
- `b` (in): The second vector.
- `n` (in): Counts dimensions, a multiple of the values per byte.
- `result` (out): The output distance value.
- `capabilities` (in): One device's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports.
- `stream` (in): Null on the CPU, or the GPU stream of that device to queue on.

Notes: The output distance value is non-negative.

Notes: The output distance value is zero if and only if the two vectors are identical.

Statuses: - [`nk_missing_kernel_k`](https://www.unum.cloud/docs/numkong/c/quickstart.md#nk_missing_kernel_k) No capability in `capabilities` has it.

## nk_angular_*_best

Angular (cosine) distance between two vectors.

```c
nk_status_t nk_angular_f64_best(
    nk_f64_t const *a,
    nk_f64_t const *b,
    nk_size_t n,
    nk_f64_t *result,
    nk_capability_t capabilities,
    nk_stream_t stream);
```

- `nk_angular_f64_best`: a, b `nk_f64_t`, result `nk_f64_t`
- `nk_angular_f32_best`: a, b `nk_f32_t`, result `nk_f64_t`
- `nk_angular_f16_best`: a, b `nk_f16_t`, result `nk_f32_t`
- `nk_angular_bf16_best`: a, b `nk_bf16_t`, result `nk_f32_t`
- `nk_angular_e4m3_best`: a, b `nk_e4m3_t`, result `nk_f32_t`
- `nk_angular_e5m2_best`: a, b `nk_e5m2_t`, result `nk_f32_t`
- `nk_angular_e2m3_best`: a, b `nk_e2m3_t`, result `nk_f32_t`
- `nk_angular_e3m2_best`: a, b `nk_e3m2_t`, result `nk_f32_t`
- `nk_angular_i8_best`: a, b `nk_i8_t`, result `nk_f32_t`
- `nk_angular_u8_best`: a, b `nk_u8_t`, result `nk_f32_t`
- `nk_angular_i4_best`: a, b `nk_i4x2_t`, result `nk_f32_t`
- `nk_angular_u4_best`: a, b `nk_u4x2_t`, result `nk_f32_t`

- `nk_angular_f64` backends: serial, neon, sve, haswell, skylake, v128relaxed, rvv, powervsx, loongsonasx
- `nk_angular_f32` backends: serial, neon, sve, haswell, skylake, v128relaxed, rvv, powervsx, loongsonasx
- `nk_angular_f16` backends: serial, neon, svehalf, haswell, skylake, diamond, v128relaxed, rvv, rvvhalf, powervsx, loongsonasx
- `nk_angular_bf16` backends: serial, neon, neonbfdot, svebfdot, haswell, genoa, v128, v128relaxed, rvv, rvvbf16, powervsx, loongsonasx
- `nk_angular_e4m3` backends: serial, neon, neonfp8, haswell, skylake, icelake, diamond, v128relaxed, rvv
- `nk_angular_e5m2` backends: serial, neon, neonfp8, haswell, skylake, diamond, v128relaxed, rvv
- `nk_angular_e2m3` backends: serial, neon, neonfp8, haswell, skylake, icelake, sierra, alder, v128relaxed
- `nk_angular_e3m2` backends: serial, neon, neonfp8, haswell, skylake, icelake, sierra, alder, v128relaxed
- `nk_angular_i8` backends: serial, neonsdot, svesdot, haswell, icelake, sierra, alder, v128, v128relaxed, rvv, powervsx, loongsonasx
- `nk_angular_u8` backends: serial, neonsdot, svesdot, haswell, icelake, sierra, alder, v128, v128relaxed, rvv, powervsx, loongsonasx
- `nk_angular_i4` backends: serial, neonsdot, icelake, rvv
- `nk_angular_u4` backends: serial, neonsdot, icelake, rvv

- `a` (in): The first vector.
- `b` (in): The second vector.
- `n` (in): Counts dimensions, a multiple of the values per byte.
- `result` (out): The output distance value.
- `capabilities` (in): One device's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports.
- `stream` (in): Null on the CPU, or the GPU stream of that device to queue on.

Notes: The output distance value is non-negative.

Notes: The output distance value is zero if and only if the two vectors are identical.

Statuses: - [`nk_missing_kernel_k`](https://www.unum.cloud/docs/numkong/c/quickstart.md#nk_missing_kernel_k) No capability in `capabilities` has it.

## nk_euclidean_output_dtype

Returns the output dtype for L2 (Euclidean) distance.

```c
nk_dtype_t nk_euclidean_output_dtype(nk_dtype_t dtype);
```

## nk_sqeuclidean_output_dtype

Returns the output dtype for L2 squared distance.

```c
nk_dtype_t nk_sqeuclidean_output_dtype(nk_dtype_t dtype);
```

## nk_angular_output_dtype

Returns the output dtype for angular/cosine distance.

```c
nk_dtype_t nk_angular_output_dtype(nk_dtype_t dtype);
```

## nk_sqeuclidean_error_bound

Returns the error bound of squared Euclidean distances, per [`nk_accumulation_error_bound`](https://www.unum.cloud/docs/numkong/c/scalar-types.md#nk_accumulation_error_bound) of their output.

```c
nk_f64_t nk_sqeuclidean_error_bound(nk_dtype_t dtype);
```

## nk_euclidean_error_bound

Returns the error bound of Euclidean distances, per [`nk_accumulation_error_bound`](https://www.unum.cloud/docs/numkong/c/scalar-types.md#nk_accumulation_error_bound) of their output.

```c
nk_f64_t nk_euclidean_error_bound(nk_dtype_t dtype);
```

## nk_angular_error_bound

Returns the error bound of angular distances, per [`nk_accumulation_error_bound`](https://www.unum.cloud/docs/numkong/c/scalar-types.md#nk_accumulation_error_bound) of their output.

```c
nk_f64_t nk_angular_error_bound(nk_dtype_t dtype);
```

## nk_spatial_sum_error_bound

Returns the error bound each term adds to the sums of a distance: none for F64 inputs, whose sums are compensated, and that of the terms themselves for the rest.

```c
nk_f64_t nk_spatial_sum_error_bound(nk_dtype_t dtype);
```

## nk_spatial_find_kernel

Finds the spatial kernel of `kind` for `dtype`, from the best of `capabilities`.

```c
nk_status_t nk_spatial_find_kernel(
    nk_kernel_kind_t kind,
    nk_dtype_t dtype,
    nk_capability_t capabilities,
    nk_kernel_punned_t *kernel,
    nk_capability_t *capability);
```

- `kind` (in): The verb whose kernel to find.
- `dtype` (in): The input type the kernel reads.
- `capabilities` (in): The mask to pick the best capability from.
- `kernel` (out): The kernel, or null when none of `capabilities` has it.
- `capability` (out): The capability the kernel belongs to, or zero.

Statuses: - [`nk_missing_kernel_k`](https://www.unum.cloud/docs/numkong/c/quickstart.md#nk_missing_kernel_k)
- [`nk_missing_library_k`](https://www.unum.cloud/docs/numkong/c/quickstart.md#nk_missing_library_k) Header-only.

