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
52 changes: 49 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,52 @@ jobs:
BUILD_RESULT: ${{ needs.linux-wheel-build.result }}
run: test "$BUILD_RESULT" = success

linux-arm64-wheel-build:
name: Linux ARM64 ${{ matrix.abi }} wheel
needs: [prepare, changes]
if: >-
!cancelled() && needs.prepare.result == 'success' &&
needs.prepare.outputs.build == 'true' &&
(needs.changes.result != 'success' || needs.changes.outputs.wheels == 'true')
runs-on: ubuntu-24.04-arm
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
abi: ${{ fromJSON(github.event_name == 'pull_request' && '["cp311","cp314"]' || '["cp311","cp312","cp313","cp314"]') }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ needs.prepare.outputs.sha }}
- uses: actions/setup-python@v7
with:
python-version: "3.14"
- name: Verify native ARM64 runner
run: test "$(uname -m)" = aarch64
- name: Build and test repaired wheel
uses: pypa/cibuildwheel@v4.2.0
env:
CIBW_BUILD: ${{ matrix.abi }}-manylinux_aarch64
with:
output-dir: wheelhouse
- name: Store wheel artifact
uses: actions/upload-artifact@v7
with:
name: linux-arm64-${{ matrix.abi }}-wheels
path: wheelhouse/*.whl
if-no-files-found: error

linux-arm64-wheels:
name: Linux ARM64 wheels (CPython 3.11–3.14)
needs: linux-arm64-wheel-build
if: ${{ always() && !cancelled() && needs.linux-arm64-wheel-build.result != 'skipped' }}
runs-on: ubuntu-latest
steps:
- name: Require all selected wheels
env:
BUILD_RESULT: ${{ needs.linux-arm64-wheel-build.result }}
run: test "$BUILD_RESULT" = success

windows-wheel-build:
name: Windows x86-64 ${{ matrix.abi }} wheel
needs: [prepare, changes]
Expand Down Expand Up @@ -193,7 +239,7 @@ jobs:

release-tag:
name: Create version tag
needs: [prepare, linux-wheels, windows-wheels, macos-wheels]
needs: [prepare, linux-wheels, linux-arm64-wheels, windows-wheels, macos-wheels]
if: >-
(github.event_name == 'push' || github.event_name == 'workflow_run') &&
needs.prepare.outputs.publish == 'true'
Expand Down Expand Up @@ -222,7 +268,7 @@ jobs:
if: >-
(github.event_name == 'push' || github.event_name == 'workflow_run') &&
needs.prepare.outputs.publish == 'true'
needs: [prepare, linux-wheels, windows-wheels, macos-wheels, release-tag]
needs: [prepare, linux-wheels, linux-arm64-wheels, windows-wheels, macos-wheels, release-tag]
runs-on: ubuntu-latest
environment:
name: pypi
Expand All @@ -249,7 +295,7 @@ jobs:
if: >-
(github.event_name == 'push' || github.event_name == 'workflow_run') &&
needs.prepare.outputs.publish == 'true'
needs: [prepare, linux-wheels, windows-wheels, macos-wheels, pypi-publish]
needs: [prepare, linux-wheels, linux-arm64-wheels, windows-wheels, macos-wheels, pypi-publish]
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,37 @@ jobs:
- name: Test downstream consumer
run: ctest --test-dir build-consumer --output-on-failure

linux-arm64:
name: Native Linux ARM64 C++
needs: changes
if: ${{ !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.cpp == 'true') }}
runs-on: ubuntu-24.04-arm
timeout-minutes: 45
env:
OMP_NUM_THREADS: "2"
steps:
- uses: actions/checkout@v7
- name: Install build tools
run: sudo apt-get update -qq && sudo apt-get install -y cmake ninja-build
- name: Configure portable ARM64 build
run: |
test "$(uname -m)" = aarch64
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DRABITQ_BUILD_TESTS=ON -DRABITQ_BUILD_SAMPLES=OFF \
-DRABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF
! grep -E -- '-mavx|_avx[25].*\.cpp' build/compile_commands.json
- name: Build and run C++ tests
run: |
cmake --build build --parallel 2
ctest --test-dir build --output-on-failure
- name: Test installed CMake package
run: |
cmake --install build --prefix "$RUNNER_TEMP/rabitq-install"
cmake -S tests/consumer -B build-consumer -G Ninja \
-DCMAKE_PREFIX_PATH="$RUNNER_TEMP/rabitq-install"
cmake --build build-consumer --parallel 2
ctest --test-dir build-consumer --output-on-failure

macos-arm64:
name: Native Apple Silicon C++
needs: changes
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ where present. Keep this file current when the paths, commands, or contracts bel
RaBitQ-Library is a C++17 library with Python bindings (`rabitqlib`) for compact vector
quantization and approximate nearest-neighbor search. It provides one-bit and multi-bit RaBitQ
encoding and IVF, HNSW, and SymphonyQG indexes. It targets x86-64 with runtime AVX2/AVX-512
dispatch and macOS ARM64 with NEON and portable scalar kernels. Linux AArch64 requires separate platform validation.
dispatch and Linux/macOS ARM64 with NEON and portable scalar kernels. Native Linux AArch64
C++ and installed-wheel CI validate that platform.
RaBitQ rotates vectors and quantizes residuals relative to centroids; compact codes and correction
factors estimate L2 distance or inner product.

Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20)

project(RaBitQLib VERSION 0.3.9 LANGUAGES CXX)
project(RaBitQLib VERSION 0.3.10 LANGUAGES CXX)

include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
Expand Down
6 changes: 3 additions & 3 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,9 @@ covers its arithmetic kernels, not every scalar loop in construction and search.

Portable wheels disable `RABITQ_ENABLE_NATIVE_OPTIMIZATION`. ARM64 builds exclude all
x86 source groups and use standard AArch64 NEON intrinsics, without Apple-only APIs.
macOS ARM64 has native C++ and installed-wheel CI. Linux ARM64 still requires separate
platform verification. The reference suites also exercise portable scalar kernels on x86, including
byte-for-byte packing parity with the selected x86 backend.
macOS and Linux ARM64 have native C++ and installed-wheel CI. The reference suites
also exercise portable scalar kernels on x86, including byte-for-byte packing parity
with the selected x86 backend.

### Change quantization or packing

Expand Down
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@

## News

- **September 2026 — Linux ARM64 support:** Native AArch64 C++ tests and
CPython 3.11–3.14 wheel builds cover NEON kernels and all three indexes.
See the [platform requirements](docs/docs/quick_start.md#requirements).

- **September 2026 — Apple Silicon support:** macOS ARM64 joins Linux and
Windows x86-64, with NEON kernels and CPython 3.11–3.14 wheel builds for
macOS 14+. See the [platform requirements](docs/docs/quick_start.md#requirements).
Expand All @@ -58,10 +62,12 @@
python -m pip install --upgrade rabitqlib
```

Wheels: CPython 3.11–3.14 on Linux and Windows x86-64, and macOS 14+ ARM64
(Apple Silicon). x86-64 uses AVX2/FMA with optional AVX-512 acceleration;
ARM64 uses NEON and portable scalar kernels. macOS wheels bundle OpenMP;
Intel Mac and universal2 wheels are not provided.
Wheels: CPython 3.11–3.14 on Linux x86-64 and ARM64, Windows x86-64, and
macOS 14+ ARM64 (Apple Silicon). x86-64 uses AVX2/FMA with optional AVX-512
acceleration; ARM64 uses NEON and portable scalar kernels. Linux ARM64 and
macOS wheels bundle OpenMP. Linux ARM64 wheels use a `manylinux_2_28` build
image; check each wheel's tags for its glibc compatibility. Intel Mac and
universal2 wheels are not provided.

## Python quick start

Expand Down Expand Up @@ -237,7 +243,7 @@ and HNSW implementations, with links to the source code.

- CMake 3.20 or newer
- a C++17 compiler with OpenMP support
- an x86-64 CPU with AVX2 and FMA, or an Apple Silicon Mac
- an x86-64 CPU with AVX2 and FMA, or an ARM64 CPU on Linux or macOS

Clone and build the library and example programs:

Expand All @@ -250,9 +256,8 @@ cmake --build build --parallel
```

For MSVC, follow the [Windows build instructions](tests/README.md#prerequisites).
For Apple Silicon source builds, see the [macOS ARM64 instructions](tests/README.md#macos-arm64).
The ARM kernel sources use portable AArch64 intrinsics, but macOS tests do not
establish Linux ARM64 support.
For ARM64 source builds, see the [Linux ARM64](tests/README.md#linux-arm64)
and [macOS ARM64](tests/README.md#macos-arm64) instructions.
Local GCC/Clang builds enable `-march=native` by default; set
`-DRABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF` for portable binaries, as release
wheels do. See [CPU dispatch details](DEVELOPMENT.md#dispatch-conventions-and-coverage)
Expand Down
5 changes: 3 additions & 2 deletions docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
accurate vector quantization and approximate nearest-neighbor search.

Build with the low-level quantizer or use complete IVF, HNSW, and SymphonyQG
indexes on Linux and Windows x86-64, or macOS ARM64 (Apple Silicon).
indexes on Linux x86-64 and ARM64, Windows x86-64, or macOS ARM64
(Apple Silicon).

[Get started](quick_start.md){ .md-button .md-button--primary }
[View on GitHub](https://github.com/VectorDB-NTU/RaBitQ-Library){ .md-button }
Expand All @@ -23,7 +24,7 @@ indexes on Linux and Windows x86-64, or macOS ARM64 (Apple Silicon).
</div>
<div class="feature-card">
<h2>Fast on modern CPUs</h2>
<p>Use AVX2/AVX-512 kernels on x86-64 and NEON on Apple Silicon. IVF and
<p>Use AVX2/AVX-512 kernels on x86-64 and NEON on ARM64. IVF and
SymphonyQG use FastScan for batched distance estimation.</p>
</div>
<div class="feature-card">
Expand Down
11 changes: 7 additions & 4 deletions docs/docs/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ a C++17 API for both indexes and low-level quantization.
| Platform | CPU baseline | Python wheel targets |
| --- | --- | --- |
| Linux x86-64 | AVX2 and FMA | CPython 3.11–3.14 |
| Linux ARM64 (AArch64) | NEON | CPython 3.11–3.14 |
| Windows x86-64 | AVX2 and FMA | CPython 3.11–3.14 |
| macOS 14+ ARM64 (Apple Silicon) | NEON | CPython 3.11–3.14 |

Expand All @@ -16,8 +17,9 @@ Windows uses MSVC (Visual Studio 2026 with the Desktop development with C++
workload and CMake 4.2+ for its generator). Apple Silicon uses AppleClang and
an external OpenMP runtime such as Homebrew `libomp`.

Linux AArch64 still requires separate platform validation. Intel Mac and
universal2 wheels are not provided.
Linux ARM64 source builds and repaired wheels run on native AArch64 CI.
The Linux ARM64 wheels use a `manylinux_2_28` build image; auditwheel assigns
the final glibc compatibility tags. Intel Mac and universal2 wheels are not provided.

<details>
<summary>CPU dispatch details</summary>
Expand All @@ -42,7 +44,8 @@ python -m pip install rabitqlib
```

Wheels target the platforms above and require no compiler or CMake.
macOS ARM64 wheels bundle OpenMP, so wheel users do not need Homebrew.
Linux ARM64 and macOS ARM64 wheels bundle OpenMP, so wheel users do not
need a separate OpenMP installation.
Release wheels disable native CPU tuning and select supported kernels at runtime.

### Build and search an IVF index
Expand Down Expand Up @@ -146,7 +149,7 @@ CMake enables native CPU tuning by default where supported by the compiler.
For portable binaries within a supported OS and architecture, configure with
`-DRABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF`; runtime kernel selection remains active.
See the [platform-specific build commands](https://github.com/VectorDB-NTU/RaBitQ-Library/blob/main/tests/README.md#quick-start)
for Windows and Apple Silicon.
for Linux ARM64, Windows, and Apple Silicon.

Example executables are written to `bin/`. Their source demonstrates complete
indexing and querying workflows:
Expand Down
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"

[project]
name = "rabitqlib"
version = "0.3.9"
version = "0.3.10"
description = "RaBitQ Python bindings for HNSW, IVF, and SymQG"
readme = "README.md"
requires-python = ">=3.11"
Expand Down Expand Up @@ -41,12 +41,16 @@ cmake.args = [
]

[tool.cibuildwheel]
build = "cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp313-manylinux_x86_64 cp314-manylinux_x86_64 cp311-win_amd64 cp312-win_amd64 cp313-win_amd64 cp314-win_amd64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 cp314-macosx_arm64"
build = "cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp313-manylinux_x86_64 cp314-manylinux_x86_64 cp311-manylinux_aarch64 cp312-manylinux_aarch64 cp313-manylinux_aarch64 cp314-manylinux_aarch64 cp311-win_amd64 cp312-win_amd64 cp313-win_amd64 cp314-win_amd64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 cp314-macosx_arm64"
build-verbosity = 1
environment = { CMAKE_ARGS = "-DRABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF" }
test-requires = ["pytest", "faiss-cpu"]
test-command = "pytest {project}/tests/python --tb=short -q"

[tool.cibuildwheel.linux]
manylinux-aarch64-image = "manylinux_2_28"
test-environment = { RABITQ_TEST_WHEEL = "1", OMP_NUM_THREADS = "2" }

[tool.cibuildwheel.macos]
archs = ["arm64"]
before-all = "brew install libomp"
Expand Down
24 changes: 21 additions & 3 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ binding tests for RaBitQ Library.
- CMake 3.20 or newer for the `ctest --test-dir` commands below (4.2 or newer
for the Visual Studio 2026 generator)
- A C++17 compiler with OpenMP support (GCC, Clang, or Visual Studio 2026)
- An x86-64 CPU with AVX2 and FMA (AVX-512 optional), or an Apple Silicon Mac
- An x86-64 CPU with AVX2 and FMA (AVX-512 optional), or an ARM64 CPU on Linux or macOS
- Git and network access during the first configuration so CMake can download
GoogleTest 1.14.0

Expand Down Expand Up @@ -45,6 +45,25 @@ cmake --build build --parallel
ctest --test-dir build --output-on-failure
```

#### Linux ARM64

Use a native AArch64 host with CMake, Ninja, a C++17 compiler, and OpenMP.
From the repository root:

```bash
test "$(uname -m)" = aarch64
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DRABITQ_BUILD_TESTS=ON -DRABITQ_BUILD_SAMPLES=OFF \
-DRABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF
cmake --build build --parallel 2
OMP_NUM_THREADS=2 ctest --test-dir build --output-on-failure
```

Native Linux ARM64 CI runs the C++ suite and installed CMake consumer. Its wheel
job builds and tests repaired CPython 3.11–3.14 wheels. Set
`RABITQ_TEST_WHEEL=1` after installing a repaired wheel locally to check the
AArch64 extension and bundled OpenMP runtime. AVX backend tests skip on ARM.

#### macOS ARM64

Use a native ARM64 project Python environment. Install CMake, Ninja and the existing
Expand All @@ -62,8 +81,7 @@ OMP_NUM_THREADS=2 ctest --test-dir build --output-on-failure

An environment providing `llvm-openmp` can use its prefix as `OpenMP_ROOT` instead.
CMake excludes AVX source files on ARM64. Reference tests run through generic and
NEON kernels; explicit AVX backend tests are guarded or skipped. Linux AArch64
needs its own build and execution validation.
NEON kernels; explicit AVX backend tests are guarded or skipped.

For Python source builds, pass `-Ccmake.define.OpenMP_ROOT=<libomp-prefix>` as well
as the native-optimization setting below. Release CI uses native `macos-14` runners,
Expand Down
33 changes: 33 additions & 0 deletions tests/python/test_wheel_linux_arm64.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Check the installed, repaired Linux ARM64 wheel and its OpenMP runtime."""

import os
import platform
import subprocess
from pathlib import Path

import pytest


@pytest.mark.skipif(
os.environ.get("RABITQ_TEST_WHEEL") != "1"
or platform.system() != "Linux"
or platform.machine() != "aarch64",
reason="Run with RABITQ_TEST_WHEEL=1 after installing a repaired Linux ARM64 wheel",
)
def test_native_aarch64_wheel_bundles_openmp():
import rabitqlib._rabitqlib as extension

path = Path(extension.__file__).resolve()
assert "site-packages" in path.parts
with path.open("rb") as binary:
header = binary.read(20)
assert header[:4] == b"\x7fELF"
assert int.from_bytes(header[18:20], "little") == 183 # EM_AARCH64

dependencies = subprocess.check_output(["ldd", str(path)], text=True)
openmp = [line for line in dependencies.splitlines() if "libgomp" in line]
assert len(openmp) == 1, dependencies
assert "=>" in openmp[0], dependencies
runtime = Path(openmp[0].split("=>", 1)[1].split("(", 1)[0].strip())
assert "site-packages" in runtime.parts, dependencies
assert runtime.is_file(), dependencies
Loading