ashvardanian / ashvardanian/NumKong
Feature: Shape-preserving N-D `nk.scale` with documented integer rounding
- 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
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