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

## bilinear

Bilinear form: aᵀ × C × b where C is a d × d matrix (row-major).

```cpp
template <numeric_dtype in_type_, numeric_dtype result_type_ = typename in_type_::curved_result_t>
status_t bilinear(
    in_type_ const *a,
    in_type_ const *b,
    in_type_ const *c,
    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_::curved_result_t, vector_of<in_type_> a_type_, vector_of<in_type_> b_type_, vector_of<in_type_> c_type_>
expected<result_type_> bilinear(
    a_type_ const & a,
    b_type_ const & b,
    c_type_ const & c,
    nk_capability_t capabilities = default_capabilities()) noexcept;
```

aᵀ × C × b over runs of `in_type_`, with `c` holding the row-major square matrix; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) when the sizes disagree or any run is strided or ends mid-value.

- `a` (in): First input vector of length d
- `b` (in): Second input vector of length d
- `c` (in): Matrix of size dxd (row-major)
- `d` (in): Number of dimensions
- `r` (out): Pointer to output 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

Notes: For weighted inner products, Mahalanobis distance, etc.

## mahalanobis

Mahalanobis distance: √((a−b)ᵀ × C × (a−b)) where C is a d × d matrix (row-major).

```cpp
template <numeric_dtype in_type_, numeric_dtype result_type_ = typename in_type_::curved_result_t>
status_t mahalanobis(
    in_type_ const *a,
    in_type_ const *b,
    in_type_ const *c,
    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_::curved_result_t, vector_of<in_type_> a_type_, vector_of<in_type_> b_type_, vector_of<in_type_> c_type_>
expected<result_type_> mahalanobis(
    a_type_ const & a,
    b_type_ const & b,
    c_type_ const & c,
    nk_capability_t capabilities = default_capabilities()) noexcept;
```

Mahalanobis distance of `a` and `b` under the row-major square matrix `c`, failing like the concept [`bilinear`](#bilinear).

- `a` (in): First input vector of length d
- `b` (in): Second input vector of length d
- `c` (in): Covariance matrix of size dxd (row-major)
- `d` (in): Number of dimensions
- `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

