# 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.hpp>`

## kld

Kullback-Leibler divergence: Σ pᵢ × log(pᵢ / qᵢ).

```cpp
template <numeric_dtype in_type_, numeric_dtype result_type_ = typename in_type_::probability_result_t>
status_t kld(
    in_type_ const *p,
    in_type_ const *q,
    std::size_t d,
    result_type_ *r,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, numeric_dtype result_type_ = typename in_type_::probability_result_t, vector_of<in_type_> a_type_, vector_of<in_type_> b_type_>
expected<result_type_> kld(
    a_type_ const & a,
    b_type_ const & b,
    nk_capability_t capabilities = default_capabilities()) noexcept;
```

Kullback-Leibler divergence of two distributions with equal dimensions; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) when they differ or either run is strided or ends mid-value.

- `p` (in): First probability distribution
- `q` (in): Second probability distribution
- `d` (in): Number of dimensions in input vectors
- `r` (out): Pointer to output divergence value
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## jsd

Jensen-Shannon distance: √(½ × (KL(p‖m) + KL(q‖m))), where m = (p + q) / 2.

```cpp
template <numeric_dtype in_type_, numeric_dtype result_type_ = typename in_type_::probability_result_t>
status_t jsd(
    in_type_ const *p,
    in_type_ const *q,
    std::size_t d,
    result_type_ *r,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, numeric_dtype result_type_ = typename in_type_::probability_result_t, vector_of<in_type_> a_type_, vector_of<in_type_> b_type_>
expected<result_type_> jsd(
    a_type_ const & a,
    b_type_ const & b,
    nk_capability_t capabilities = default_capabilities()) noexcept;
```

Jensen-Shannon divergence of two distributions, failing like the concept [`kld`](#kld).

- `p` (in): First probability distribution
- `q` (in): Second probability distribution
- `d` (in): Number of dimensions in input vectors
- `r` (out): Pointer to output distance value
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

