Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ semantic versioning while it remains in the 0.x development series.
- The public `rosa` imports and existing stateful inference APIs are unchanged.
- The distribution remains `rosa-torch` and supports Python 3.10+.
- The optional `rosa-torch-native 0.3.0` companion remains separately versioned
and requires `rosa-torch[numba]>=0.3,<0.4`.
and requires `rosa-torch>=0.3,<0.4` plus NumPy. The Numba extra remains
optional for suffix-automaton and rich-candidate integration.

## 0.2.0 — 2026-08-11

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,15 @@ For the lowest CPU step latency, install the optional native companion. Wheels
are published separately for each supported platform and Python ABI:

```bash
uv add rosa-torch-native
uv add 'rosa-torch[numba]' rosa-torch-native
```

The native sources remain available from the Git repository and are not
included in the pure-Python `rosa-torch` source distribution.

The stateful backend detects it lazily and otherwise falls back to Numba.
The native RLBWT backends require only the base package and NumPy; the Numba
extra enables the native suffix-automaton and rich-candidate integration.

Install the package and its locked development dependencies with [uv](https://docs.astral.sh/uv/):

Expand Down
8 changes: 5 additions & 3 deletions native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ main Python package.
The C++ core implements the validated exact production state machine. It binds
the NumPy arrays of a `_StatefulInferenceState` once, updates them in place,
and releases the GIL during computation. It neither includes nor calls
libtorch. The runtime dependency `rosa-torch[numba]>=0.3,<0.4` provides the
compatible state contract together with PyTorch, NumPy, and Numba.
libtorch. The runtime dependencies `rosa-torch>=0.3,<0.4` and NumPy provide the
compatible tensor and array contracts. Install the `rosa-torch[numba]` extra
to enable the native suffix-automaton and rich-candidate integration; the
native RLBWT backends do not require Numba.

The constructor validates every shape, dtype, counter, and ABI version before
retaining any pointer. The current native state ABI is `1`.
Expand Down Expand Up @@ -47,7 +49,7 @@ wheel does not provide a newer optional method.
Install the matching wheel for the current platform and Python ABI from PyPI:

```bash
uv add rosa-torch-native
uv add 'rosa-torch[numba]' rosa-torch-native
```

`rosa-torch` detects the extension automatically from its Numba inference
Expand Down
3 changes: 2 additions & 1 deletion native/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ classifiers = [
"Topic :: Scientific/Engineering :: Artificial Intelligence",
]
dependencies = [
"rosa-torch[numba]>=0.3,<0.4",
"numpy>=1.24",
"rosa-torch>=0.3,<0.4",
]

[project.urls]
Expand Down
83 changes: 79 additions & 4 deletions native/src/rosa_native_step.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,80 @@ namespace {

constexpr size_t kMaximumNativeThreads = 16;

uint32_t leading_zero_count64(uint64_t value) noexcept {
if (value == 0)
return 64;
#if defined(_MSC_VER)
uint32_t count = 0;
if ((value & UINT64_C(0xffffffff00000000)) == 0) {
count += 32;
value <<= 32;
}
if ((value & UINT64_C(0xffff000000000000)) == 0) {
count += 16;
value <<= 16;
}
if ((value & UINT64_C(0xff00000000000000)) == 0) {
count += 8;
value <<= 8;
}
if ((value & UINT64_C(0xf000000000000000)) == 0) {
count += 4;
value <<= 4;
}
if ((value & UINT64_C(0xc000000000000000)) == 0) {
count += 2;
value <<= 2;
}
return count + ((value & UINT64_C(0x8000000000000000)) == 0 ? 1 : 0);
#else
return static_cast<uint32_t>(__builtin_clzll(value));
#endif
}

uint32_t trailing_zero_count64(uint64_t value) noexcept {
if (value == 0)
return 64;
#if defined(_MSC_VER)
uint32_t count = 0;
if ((value & UINT64_C(0x00000000ffffffff)) == 0) {
count += 32;
value >>= 32;
}
if ((value & UINT64_C(0x000000000000ffff)) == 0) {
count += 16;
value >>= 16;
}
if ((value & UINT64_C(0x00000000000000ff)) == 0) {
count += 8;
value >>= 8;
}
if ((value & UINT64_C(0x000000000000000f)) == 0) {
count += 4;
value >>= 4;
}
if ((value & UINT64_C(0x0000000000000003)) == 0) {
count += 2;
value >>= 2;
}
return count + ((value & UINT64_C(0x1)) == 0 ? 1 : 0);
#else
return static_cast<uint32_t>(__builtin_ctzll(value));
#endif
}

uint32_t population_count64(uint64_t value) noexcept {
#if defined(_MSC_VER)
value -= (value >> 1) & UINT64_C(0x5555555555555555);
value = (value & UINT64_C(0x3333333333333333)) +
((value >> 2) & UINT64_C(0x3333333333333333));
value = (value + (value >> 4)) & UINT64_C(0x0f0f0f0f0f0f0f0f);
return static_cast<uint32_t>((value * UINT64_C(0x0101010101010101)) >> 56);
#else
return static_cast<uint32_t>(__builtin_popcountll(value));
#endif
}

size_t native_thread_count(int64_t rows) {
if (rows <= 1)
return 1;
Expand Down Expand Up @@ -2291,7 +2365,8 @@ class NativeRLBWTState {
matched += kChunkCodes;
continue;
}
return matched + static_cast<size_t>(__builtin_clzll(difference) / 4);
return matched +
static_cast<size_t>(leading_zero_count64(difference) / 4);
}
size_t edge = contiguous;
while (edge != 0) {
Expand Down Expand Up @@ -3342,7 +3417,7 @@ class NativeRLBWTState {
}

static uint32_t popcount64(uint64_t value) noexcept {
return static_cast<uint32_t>(__builtin_popcountll(value));
return population_count64(value);
}
static size_t compact_histogram_rank(const Leaf &leaf,
uint32_t code) noexcept {
Expand Down Expand Up @@ -3477,7 +3552,7 @@ class NativeRLBWTState {
for (uint32_t word_index = 0; word_index < 4; ++word_index) {
uint64_t word = leaf.histogram_bitmap[word_index];
while (word != 0) {
const uint32_t bit = static_cast<uint32_t>(__builtin_ctzll(word));
const uint32_t bit = trailing_zero_count64(word);
const uint32_t code = word_index * 64u + bit;
histogram_set(result, index++, code,
leaf_histogram_count(leaf, code, source_width),
Expand Down Expand Up @@ -4215,7 +4290,7 @@ class NativeRLBWTState {
for (uint32_t word_index = 0; word_index < 4; ++word_index) {
uint64_t word = leaf.histogram_bitmap[word_index];
while (word != 0) {
const uint32_t bit = static_cast<uint32_t>(__builtin_ctzll(word));
const uint32_t bit = trailing_zero_count64(word);
const uint32_t code = word_index * 64u + bit;
add_histogram_entry(destination, code,
leaf_histogram_count(leaf, code, width), width);
Expand Down
Loading
Loading