# Probability Metrics

> NumKong for C

Probability kernels target divergences directly instead of making you rebuild them from scalar loops.

```c
nk_f32_t p[] = {0.2f, 0.3f, 0.5f}, q[] = {0.1f, 0.3f, 0.6f};
nk_f64_t kl_forward = 0, kl_reverse = 0, js_forward = 0, js_reverse = 0;

nk_kld_f32_best(p, q, 3, &kl_forward, capabilities, NULL);
nk_kld_f32_best(q, p, 3, &kl_reverse, capabilities, NULL);
assert(kl_forward != kl_reverse && "KLD is asymmetric");

nk_jsd_f32_best(p, q, 3, &js_forward, capabilities, NULL);
nk_jsd_f32_best(q, p, 3, &js_reverse, capabilities, NULL);
assert(js_forward == js_reverse && "JSD is symmetric");
```

These paths are useful once you move below `f64`.
Naive implementations are usually dominated by repeated scalar transcendental calls and weak accumulation policy.

`#include <numkong/probability.h>`

## nk_kld_*_best

Kullback-Leibler divergence between two discrete probability distributions.

```c
nk_status_t nk_kld_f16_best(
    nk_f16_t const *a,
    nk_f16_t const *b,
    nk_size_t n,
    nk_f32_t *result,
    nk_capability_t capabilities,
    nk_stream_t stream);
```

- `nk_kld_f16_best`: a, b `nk_f16_t`, result `nk_f32_t`
- `nk_kld_bf16_best`: a, b `nk_bf16_t`, result `nk_f32_t`
- `nk_kld_f32_best`: a, b `nk_f32_t`, result `nk_f64_t`
- `nk_kld_f64_best`: a, b `nk_f64_t`, result `nk_f64_t`

- `nk_kld_f16` backends: serial, neon, haswell, skylake, rvv
- `nk_kld_bf16` backends: serial, rvv
- `nk_kld_f32` backends: serial, neon, haswell, skylake, rvv
- `nk_kld_f64` backends: serial, neon, haswell, skylake, rvv

- `a` (in): The first discrete probability distribution.
- `b` (in): The second discrete probability distribution.
- `n` (in): The number of elements in the distributions.
- `result` (out): The output divergence 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 distributions are assumed to be normalized.

Notes: The output divergence value is non-negative.

Notes: The output divergence value is zero if and only if the two distributions 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_jsd_*_best

Jensen-Shannon distance between two discrete probability distributions.

```c
nk_status_t nk_jsd_f16_best(
    nk_f16_t const *a,
    nk_f16_t const *b,
    nk_size_t n,
    nk_f32_t *result,
    nk_capability_t capabilities,
    nk_stream_t stream);
```

- `nk_jsd_f16_best`: a, b `nk_f16_t`, result `nk_f32_t`
- `nk_jsd_bf16_best`: a, b `nk_bf16_t`, result `nk_f32_t`
- `nk_jsd_f32_best`: a, b `nk_f32_t`, result `nk_f64_t`
- `nk_jsd_f64_best`: a, b `nk_f64_t`, result `nk_f64_t`

- `nk_jsd_f16` backends: serial, neon, haswell, skylake, rvv
- `nk_jsd_bf16` backends: serial, rvv
- `nk_jsd_f32` backends: serial, neon, haswell, skylake, rvv
- `nk_jsd_f64` backends: serial, neon, haswell, skylake, rvv

- `a` (in): The first discrete probability distribution.
- `b` (in): The second discrete probability distribution.
- `n` (in): The number of elements in the distributions.
- `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 distributions are assumed to be normalized.

Notes: The output distance value is non-negative.

Notes: The output distance value is zero if and only if the two distributions 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_probability_output_dtype

Returns the output dtype for probability measures (KLD, JSD).

```c
nk_dtype_t nk_probability_output_dtype(nk_dtype_t dtype);
```

## nk_probability_term_dtype

Returns the type the ratio and logarithm of every term are computed in.

```c
nk_dtype_t nk_probability_term_dtype(nk_dtype_t dtype);
```

## nk_probability_error_bound

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

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

## nk_probability_find_kernel

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

```c
nk_status_t nk_probability_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.

