ashvardanian / ashvardanian/NumKong

Feature: Shape-preserving N-D `nk.scale` with documented integer rounding

Open
#327 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
C
Stars
1.9k
Forks
130
Avg merge
18h 28m
Merged PRs (30d)
3

Description

## Current behavior

The original rounding problem is fixed in NumKong 7.7.0. `nk.scale` now matches round-to-nearest-even followed by saturation for the tested `uint8` half-integer cases:

```python
import numkong as nk
import numpy as np

x = np.array([1, 3, 5, 7, 9], dtype=np.uint8)
print(np.asarray(nk.scale(x, alpha=1.5, beta=0)))
# [2, 4, 8, 10, 14]
```

The remaining limitation is shape handling. `nk.scale` still operates on a vector and does not preserve an arbitrary input shape. Albucore currently flattens an image or volume, calls `nk.scale`, and reshapes the result.

## Request

Extend `nk.scale` to accept arbitrary-rank buffers and preserve their shape:

```python
out = nk.scale(x, alpha=1.5, beta=-20.0)
assert out.shape == x.shape
```

The initial scope can keep `alpha` and `beta` scalar. Broadcasted parameters and mixed input/output dtypes are tracked separately in #315.

Required behavior:

- Accept contiguous HWC, NHWC/DHWC, and NDHWC arrays without flattening.
- Preserve the input shape.
- Accept an `out=` buffer with the same shape.
- Handle strided inputs when the Tensor walking machinery supports them.
- Document integer conversion as round-to-nearest-even followed by saturation, or document another exact rule if that is not the intended contract.
- Test half-integers, negative results, overflow, NaN, and infinity for every supported integer output type.

## Why this is useful

Albucore uses scalar affine transforms on images, videos, and volumes. For contiguous data, flattening hides shape validation and requires a reshape step. The workaround does not extend naturally to strided views.

The current Albucore benchmark compares `nk.scale`, StringZilla LUTs, and `cv2.LUT` across HWC, DHWC, and NDHWC layouts: https://github.com/albumentations-team/albucore/blob/main/benchmarks/results/opencv5-routing/benchmark_scale_vs_lut.md

## Acceptance example

```python
rng = np.random.default_rng(137)
x = rng.integers(0, 256, size=(2, 16, 128, 160, 3), dtype=np.uint8)
expected = np.clip(np.rint(x.astype(np.float32) * 1.3 + 30.0), 0, 255).astype(np.uint8)
actual = np.asarray(nk.scale(x, alpha=1.3, beta=30.0))
np.testing.assert_array_equal(actual, expected)
```

Contributor guide

Open the contributing guide

Research direction

Start at the nk.scale entry point and inspect the Tensor walking machinery that handles vector and strided inputs. Compare behavior with the Albucore benchmark at benchmarks/results/opencv5-routing/benchmark_scale_vs_lut.md, then run the acceptance example and add coverage for shapes, out= buffers, and integer edge cases. Done means arbitrary-rank shapes are preserved and the documented rounding and saturation rules pass for supported integer outputs.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, numpy, python
Domain
data, performance, testing
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.