Releases: adamdeprince/stride-align
Release list
v0.5.0
v0.4.0
Added
-
Phonetic encoders (Phase D.2). Full standard family with the
same byte-extraction + variant pattern as the rest of the library:- Soundex (Russell & Odell, 1918).
soundex,soundex_equal. - Metaphone (Lawrence Philips, 1990) with
MetaphoneVariant
enum —PHILIPS(spec-correct, Apache Commons Codec branch)
andJELLYFISH(matches the popular Python library).metaphone,
metaphone_equal. - Double Metaphone (Lawrence Philips, 2000) with
DoubleMetaphoneVariantenum —COMMONS(faithful Apache
Commons Codec /doublemetaphonePyPI port) andPYTHON
(bug-compat with themetaphonePyPI package's missing-else GH
leak). Returns(primary, alternate).double_metaphone. - NYSIIS (Taft, 1970).
nysiis,nysiis_equal. - Match Rating Approach (Moore, Western Airlines, 1977).
match_rating_codex,match_rating_compare. - Caverphone 2.0 (Hood, 2004).
caverphone.
All encoders dispatch through the same
peel_to_ascii_string
helper, acceptstr/bytesinterchangeably, and skip non-letter /
non-ASCII codepoints. Cross-checked against Apache Commons Codec
reference data and thejellyfish,metaphone,doublemetaphone,
andpyphoneticsPyPI packages. - Soundex (Russell & Odell, 1918).
-
Dynamic Time Warping (
dtw,dtw_distances). Sakoe-Chiba band
viawindow=(absolute integer radius or fractional0 < r <= 1);
L1 vs L2-squared distance auto-picked by dtype (int16-> L1,
float32/float64-> L2-squared) with explicitdistance=
override. Inputs are NumPyndarray; matching dtype enforced. -
cdist_top_k_per_query. Generator API:
for query, [(score, target), ...] in cdist_top_k_per_query(...).
Per-query top-k heap rather than the global top-k that
cdist_top_kreturns.pruning=Falseby default;pruning=True
enables adaptive length-difference pruning using the same
max_normalized_similarityclosed-form bound as
cdist_above_threshold. Worst-in-heap tightens as scoring
progresses, so targets whose bound can't beat the current heap
minimum skip the kernel entirely. ~12× faster than the unpruned
baseline on wide-length workloads in benchmarks; flat on tight-
length workloads. Hamming length-mismatch pairs are skipped via
the bound rather than raisingValueError. -
Wide-token Farrar batch (Phase B.2.x). Full SW + NW Farrar
dispatch through the wide-token kernel for NumPyndarraywith
uint16dtype andstrinputs with codepoints > 255 (UCS-2).
Auto-promotes at the binding layer; 8-bit byte kernel stays
byte-stable for ASCII / Latin-1 / bytes workloads. -
Wider PyPI metadata.
[project.urls](Homepage / Documentation /
Repository / Source / Download / Issues / Changelog / Benchmarks),
maintainers,license-files = ["LICENSE"](PEP 639),
Typing :: Typedclassifier, Natural Language classifiers for
English and Chinese (Simplified), Operating System classifiers for
Linux and macOS, and named extras forbench(rapidfuzz,
Levenshtein,editdistance,parasail) andphonetic-compat
(jellyfish,metaphone,doublemetaphone,pyphonetics).
Changed
-
Python 3.12+ floor.
requires-python = ">=3.12", dropping
3.9 / 3.10 / 3.11. The nanobind 2.x vectorcall fast path is
unconditional from this version; the dispatch layer no longer
needs the dict-lookup workarounds that replaced 3.10'smatch
statements. -
Vectorcall: skip the Python wrapper frame for every cheap
function. Phonetic encoders, Jaro / Jaro-Winkler scalar entry
points, the scalar Levenshtein / OSA / Hamming / Indel / true-DL /
DTW entry points,*_top_k,extract, andextract_bestare now
direct re-exports of the C++ binding rather than Python forwarder
wrappers. Tight-loop savings measured at 22-54% per call on the
encoders and 22-26% on Levenshtein-family scoring. Docstrings
moved to the C++ binding side sohelp(sa.soundex)etc. still
works. -
str/bytesrejection on batch inputs moved to C++. The
reject_str_or_bytes_targetshelper fires from a single shared
point inpreprocess.hpp; the per-binding_coerce_targetsPython
helper is gone. SameTypeError, fewer Python frames.
Fixed
-
Generator double-iteration in
*_top_kandextract. The
bindings calledPySequence_Fastto grabitemsformake_top_k,
then passed the original handle to the score dispatcher — which
re-materialised it. Generators were getting consumed by the first
pass and the second returned empty. The dispatch calls now take
the already-materialisedowner. Surfaced by the wrapper-removal
pass that exposed the underlying behaviour. -
Double Metaphone alternate cleanup. Empty alternate (matching
themetaphoneanddoublemetaphonePyPI conventions) when the
primary and alternate codes are identical, soif alt:works.
Notes
- No abi3 wheel. The Stable ABI excludes the
cpython/unicodeobject.hfast macros (PyUnicode_KIND,
PyUnicode_*_DATA,PyUnicode_GET_LENGTH) that the zero-copy
UCS-1/UCS-2/UCS-4 path relies on for the CJK / wide-token SIMD
kernels. Their function-form equivalents either allocate a copy or
are one call per codepoint, both fatal to the SIMD path. We ship
one wheel per minor Python version (3.12 / 3.13 / 3.14) instead.
Rationale recorded inCMakeLists.txt.
stride-align 0.3.0
stride-align 0.3.0
Highlights (full details in CHANGELOG.md):
New scorers
- Indel distance (
Scorer.INDEL/Scorer.INDEL_NORMALIZED) — Levenshtein restricted to insertions and deletions; bit-parallel Allison-Dix single-word kernel. - True (unrestricted) Damerau-Levenshtein (
Scorer.TRUE_DAMERAU_LEVENSHTEIN/Scorer.TRUE_DAMERAU_LEVENSHTEIN_NORMALIZED) — the unrestricted form where a character may participate in multiple edits. ExistingScorer.DAMERAU_LEVENSHTEINcontinues to refer to OSA.
cdist pruning + cutoff push-down
- Length-difference pruning for
cdist_above_thresholdandcdist_top_k. - Row-sort by query length in
cdist_top_k. - Per-pair cutoff push-down into the Myers (Lev), OSA, and Hamming SIMD inner loops.
- Cross-arch geomean 426x speedup at threshold 0.99 vs the unpruned baseline (24 scorer×host cells, median 512x, range 145x–1408x). See BENCHMARK.md and the Loongson vs Tiger Lake report (简体中文).
Fixes
- macOS / LoongArch64
cdist_above_thresholditerator —nb::stop_iterationacross DSO boundaries was surfacing asRuntimeErrorinstead ofStopIteration. Now uses the Python C API (PyErr_SetNone(PyExc_StopIteration)+ nullnb::objectreturn). Fixes 91 macOS test failures.
Build / packaging
- Python 3.9 support (was 3.10+). The three
matchblocks became dict lookups; everything else was already 3.9-compatible. - C++23 → C++20 build requirement. The codebase didn't actually use any C++23 stdlib feature; lowering lets older toolchains (gcc 10) build the project. Two libstdc++-10 gaps (
std::bit_cast,std::make_unique_for_overwrite) bridged with feature-test-gated fallbacks. See docs/power8-gcc10-workarounds.md.
Distribution
-
PyPI (
pip install stride-align): sdist + 23 wheels —- 6 manylinux_2_27 x86_64 (cp39–cp314)
- 6 musllinux_1_2 x86_64 (cp39–cp314)
- 5 manylinux_2_39 aarch64 (cp310–cp314)
- 6 macOS arm64 (cp39–cp314)
-
This GitHub release: only the Linux LoongArch64 wheel (cp313), which PyPI's platform-tag list doesn't yet accept. Install with:
pip install \ https://github.com/adamdeprince/stride-align/releases/download/v0.3.0/stride_align-0.3.0-cp313-cp313-linux_loongarch64.whl
See README §安装 for the full Loongson install recipe.
docs
- New: docs/adding-a-new-algorithm.md, docs/power8-gcc10-workarounds.md, docs/loongson-vs-tiger-lake-cdist-2026-05-24.md.
- README: Simplified Chinese translation at README.zh-CN.md.
v0.2.0
Levenshtein + Damerau-Levenshtein, multi-arch SIMD batch.
What's new since v0.1.0
- Multi-word Levenshtein SIMD for patterns up to 256 chars (W = 2/3/4).
score_cutoffparameter on everylevenshtein_*function. Per-lane done masks + all-lanes early-exit; up to 6x rapidfuzz on cutoff workloads.- Zero-copy singular dispatch for bytes / 1-byte unicode (
levenshtein_score(q, t)skips theprepare_alignmentvector copy). - Damerau-Levenshtein (OSA-restricted, Hyyrö 2002) — scalar bit-parallel + SIMD batch. New API:
damerau_levenshtein_score,_normalized_score,_scores,_normalized_scores. - NeonOps / LsxOps / LasxOps / VsxOps Ops bundles. Combined with the existing x86 SSE/AVX2/AVX-512/AVX10 bundles, every SIMD ISA we support now has a multi-target Levenshtein and Damerau-Levenshtein kernel:
- Intel SSE4.1 / AVX2 / AVX-512 / AVX10-256 / AVX10-512
- Apple Silicon NEON (macOS arm64)
- AArch64 NEON / SVE / SVE2 (Linux)
- LoongArch LSX / LASX
- PowerPC VSX (Power8+)
Highlight benchmarks vs rapidfuzz / python-Levenshtein
| arch | workload | ratio |
|---|---|---|
| Intel AVX-512 | Lev medium q=100, 1-vs-200 | 3.03x rapidfuzz |
| Intel AVX-512 | Damerau short q=20, 1-vs-1000 | 4.22x rapidfuzz OSA |
| Intel AVX-512 | Lev cutoff q=50, K=12 | 6.03x rapidfuzz |
| Mac M4 NEON | Lev short q=30, 1-vs-1000 | 8.54x python-Levenshtein |
| Mac M4 NEON | Damerau short q=30, 1-vs-1000 | 7.45x rapidfuzz OSA |
| Graviton4 (NEON/SVE/SVE2) | Lev short q=30, 1-vs-1000 | 4.05x python-Levenshtein |
| Power8 VSX | Lev multi-word q=200, 1-vs-200 | 3.03x vs generic |
| Loongson LASX | Lev multi-word q=100, 1-vs-200 | 3.34x vs generic |
Full benchmarks in BENCHMARK.md.
Installation
```bash
pip install stride-align==0.2.0
```
PyPI has wheels for Python 3.10-3.14 on Linux x86_64 (manylinux_2_28), Linux aarch64 (manylinux_2_39), Linux ppc64le (manylinux_2_34), and macOS arm64. LoongArch64 wheels are attached to this release (PyPI doesn't accept the linux_loongarch64 platform tag); install with:
```bash
PY=$(python3 -c 'import sys; print(f"cp{sys.version_info.major}{sys.version_info.minor}")')
pip install https://github.com/adamdeprince/stride-align/releases/download/v0.2.0/stride_align-0.2.0-\${PY}-\${PY}-linux_loongarch64.whl
```
stride-align 0.1.0
stride-align v0.1.0 — first PyPI release.
Most wheels are on PyPI: https://pypi.org/project/stride-align/0.1.0/
LoongArch64 wheels live here on GitHub Releases because PyPI does not
yet accept the linux_loongarch64 or manylinux_2_38_loongarch64
platform tags. Install on Loongson with:
PY=$(python3 -c 'import sys; print(f"cp{sys.version_info.major}{sys.version_info.minor}")')
pip install https://github.com/adamdeprince/stride-align/releases/download/v0.1.0/stride_align-0.1.0-${PY}-${PY}-linux_loongarch64.whlAvailable wheels (all five SIMD backends inside — LSX + LASX + generic + SWAR):
- Python 3.10 / LoongArch64
- Python 3.11 / LoongArch64
- Python 3.12 / LoongArch64
- Python 3.13 / LoongArch64
- Python 3.14 / LoongArch64
Built on Loongson 3A6000-class hardware (Kylin V10 SP1, GCC 15.2.0,
CMake 4.3.2). See BENCHMARK.md for performance numbers.