# Curved Metrics

> NumKong for C

Curved-space kernels are separate from the flat Euclidean family because their dataflow is different.
They combine vectors with an extra metric tensor or covariance inverse.

```c
// Complex bilinear form: aᴴ M b
nk_f32c_t a[32], b[32], metric[32 * 32];
nk_f64c_t result = {0, 0};
nk_bilinear_f32c_best(a, b, metric, 32, &result, capabilities, NULL);

// Real Mahalanobis distance: √((a−b)ᵀ M⁻¹ (a−b))
nk_f32_t x[64], y[64], inv_cov[64 * 64];
nk_f64_t distance = 0;
nk_mahalanobis_f32_best(x, y, inv_cov, 64, &distance, capabilities, NULL);
```

`#include <numkong/curved.h>`

## nk_bilinear_*_best

Bilinear form between vectors a and b under metric tensor C.

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

- `nk_bilinear_f64_best`: a, b, c `nk_f64_t`, result `nk_f64_t`
- `nk_bilinear_f32_best`: a, b, c `nk_f32_t`, result `nk_f64_t`
- `nk_bilinear_f16_best`: a, b, c `nk_f16_t`, result `nk_f32_t`
- `nk_bilinear_bf16_best`: a, b, c `nk_bf16_t`, result `nk_f32_t`

- `nk_bilinear_f64` backends: serial, neon, smef64, haswell, skylake, rvv
- `nk_bilinear_f32` backends: serial, neon, smef64, haswell, skylake, rvv
- `nk_bilinear_f16` backends: serial, neon, haswell, rvv
- `nk_bilinear_bf16` backends: serial, neonbfdot, haswell, genoa, rvv

Computes aᵀ × C × b = Σᵢ Σⱼ aᵢ × cᵢⱼ × bⱼ

- `a` (in): The first vector.
- `b` (in): The second vector.
- `c` (in): The metric tensor or covariance matrix, stored row-major as an n × n matrix.
- `n` (in): The number of dimensions in the vectors.
- `result` (out): The output bilinear form 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 value can be negative.

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_mahalanobis_*_best

Mahalanobis distance between vectors a and b under metric tensor C.

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

- `nk_mahalanobis_f64_best`: a, b, c `nk_f64_t`, result `nk_f64_t`
- `nk_mahalanobis_f32_best`: a, b, c `nk_f32_t`, result `nk_f64_t`
- `nk_mahalanobis_f16_best`: a, b, c `nk_f16_t`, result `nk_f32_t`
- `nk_mahalanobis_bf16_best`: a, b, c `nk_bf16_t`, result `nk_f32_t`

- `nk_mahalanobis_f64` backends: serial, neon, smef64, haswell, skylake, rvv
- `nk_mahalanobis_f32` backends: serial, neon, smef64, haswell, skylake, rvv
- `nk_mahalanobis_f16` backends: serial, neon, haswell, rvv
- `nk_mahalanobis_bf16` backends: serial, neonbfdot, haswell, genoa, rvv

Computes √((a-b)ᵀ × C × (a-b)) = √(Σᵢ Σⱼ (aᵢ-bᵢ) × cᵢⱼ × (aⱼ-bⱼ))

- `a` (in): The first vector.
- `b` (in): The second vector.
- `c` (in): The Positive Semi-Definite (PSD) matrix, stored row-major as an n × n matrix.
- `n` (in): The number of dimensions in the vectors.
- `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 value is non-negative when C is PSD.

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

Notes: The matrix C must be positive semi-definite. If C is not PSD, the quadratic form (a-b)ᵀ C (a-b) may be negative, and the square root will produce NaN.

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_bilinear_*_best

Complex bilinear form between vectors a and b under metric tensor C.

```c
nk_status_t nk_bilinear_f64c_best(
    nk_f64c_t const *a,
    nk_f64c_t const *b,
    nk_f64c_t const *c,
    nk_size_t n,
    nk_f64c_t *results,
    nk_capability_t capabilities,
    nk_stream_t stream);
```

- `nk_bilinear_f64c_best`: a, b, c `nk_f64c_t`, results `nk_f64c_t`
- `nk_bilinear_f32c_best`: a, b, c `nk_f32c_t`, results `nk_f64c_t`
- `nk_bilinear_f16c_best`: a, b, c `nk_f16c_t`, results `nk_f32c_t`
- `nk_bilinear_bf16c_best`: a, b, c `nk_bf16c_t`, results `nk_f32c_t`

- `nk_bilinear_f64c` backends: serial, smef64, skylake
- `nk_bilinear_f32c` backends: serial, neon, smef64, skylake
- `nk_bilinear_f16c` backends: serial, neon
- `nk_bilinear_bf16c` backends: serial, neonbfdot, genoa

- `a` (in): The first complex vector.
- `b` (in): The second complex vector.
- `c` (in): The complex metric tensor, stored row-major as an n × n matrix.
- `n` (in): The number of dimensions in the vectors.
- `results` (out): The output complex value with real and imaginary parts.
- `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.

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_bilinear_output_dtype

Returns the output dtype for bilinear forms.

```c
nk_dtype_t nk_bilinear_output_dtype(nk_dtype_t dtype);
```

## nk_mahalanobis_output_dtype

Returns the output dtype for Mahalanobis metrics.

```c
nk_dtype_t nk_mahalanobis_output_dtype(nk_dtype_t dtype);
```

## nk_bilinear_error_bound

Returns the error bound of bilinear forms, 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_bilinear_error_bound(nk_dtype_t dtype);
```

## nk_mahalanobis_error_bound

Returns the error bound of Mahalanobis metrics, 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_mahalanobis_error_bound(nk_dtype_t dtype);
```

## nk_curved_find_kernel

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

```c
nk_status_t nk_curved_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 no capability in `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.

