From 3b8f5fb4c7b0ac11e7488d540d1f59ad20dd1af5 Mon Sep 17 00:00:00 2001 From: Bradley Dice Date: Sat, 10 Oct 2026 15:14:43 +0000 Subject: [PATCH 1/3] [DO NOT MERGE] Reduce CI to cuvs-lucene and add diagnostics for missing filtered results --- .github/workflows/pr.yaml | 705 +----------------- ci/test_lucene.sh | 26 +- ...stDiagFilteredMultiPartitionShortfall.java | 150 ++++ 3 files changed, 181 insertions(+), 700 deletions(-) create mode 100644 java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestDiagFilteredMultiPartitionShortfall.java diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index a6438c1a8d..0d0fd381b4 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -11,30 +11,10 @@ jobs: pr-builder: needs: - build-details - - check-nightly-ci - - changed-files - - checks - - check-c-abi - - security-suite - conda-cpp-build - - conda-cpp-tests - - conda-cpp-checks - - conda-python-build - - conda-python-tests - - rocky8-clib-standalone-build - - rocky8-clib-tests - conda-java-build - - conda-java-tests - conda-lucene-build - conda-lucene-tests - - rust-build - - go-build - - docs-build - - docs-build-matrix - - wheel-build-libcuvs - - wheel-build-cuvs - - wheel-tests-cuvs - - devcontainer - telemetry-setup uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@main permissions: @@ -55,406 +35,8 @@ jobs: uses: rapidsai/shared-actions/telemetry-dispatch-stash-base-env-vars@main build-details: uses: rapidsai/shared-workflows/.github/workflows/compute-build-details.yaml@main - check-nightly-ci: - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - id-token: write - pull-requests: read - env: - GH_TOKEN: ${{ github.token }} - steps: - - name: Get PR Info - id: get-pr-info - uses: nv-gha-runners/get-pr-info@main - - name: Check if nightly CI is passing - uses: rapidsai/shared-actions/check_nightly_success/dispatch@main - with: - repo: ${{ github.repository }} - target-branch: ${{ fromJSON(steps.get-pr-info.outputs.pr-info).base.ref }} - max-days-without-success: 30 - changed-files: - needs: telemetry-setup - permissions: - actions: read - contents: read - packages: read - pull-requests: read - uses: rapidsai/shared-workflows/.github/workflows/changed-files.yaml@main - with: - files_yaml: | - build_docs: - - '**' - - '!**/*/REVIEW_GUIDELINES.md' - - '!.coderabbit.yaml' - - '!.devcontainer/**' - - '!.github/CODEOWNERS' - - '!.github/ISSUE_TEMPLATE/**' - - '!.github/PULL_REQUEST_TEMPLATE.md' - - '!.github/copy-pr-bot.yaml' - - '!.github/labeler.yml' - - '!.github/ops-bot.yaml' - - '!.github/release.yml' - - '!.github/workflows/build.yaml' - - '!.github/workflows/check-c-abi.yaml' - - '!.github/workflows/labeler.yml' - - '!.github/workflows/publish-rust.yaml' - - '!.github/workflows/security-suite.yml' - - '!.github/workflows/store-c-abi-baseline.yaml' - - '!.github/workflows/test.yaml' - - '!.github/workflows/trigger-breaking-change-alert.yaml' - - '!.github/workflows/update-c-abi-baseline.yaml' - - '!.pre-commit-config.yaml' - - '!.shellcheckrc' - - '!.yamllint.yaml' - - '!Dockerfile' - - '!SECURITY.md' - - '!ci/build_wheel*.sh' - - '!ci/check_style.sh' - - '!ci/release/update-version.sh' - - '!ci/run*.sh' - - '!ci/test*.sh' - - '!ci/validate_wheel.sh' - - '!thirdparty/LICENSES/**' - test_cpp: - - '.github/workflows/pr.yaml' - - 'RAPIDS_BRANCH' - - 'VERSION' - - 'build.sh' - - 'c/**' - - 'ci/build_cpp.sh' - - 'ci/get_test_data.sh' - - 'ci/test_cpp.sh' - - 'cmake/**' - - 'conda/recipes/libcuvs/**' - - 'cpp/**' - - 'dependencies.yaml' - - '!**/*/REVIEW_GUIDELINES.md' - - '!cpp/doxygen/**' - test_standalone_c: - - '.github/workflows/pr.yaml' - - 'RAPIDS_BRANCH' - - 'VERSION' - - 'build.sh' - - 'c/**' - - 'ci/standalone_c/**' - - 'ci/test_standalone_c.sh' - - 'cmake/**' - - 'cpp/**' - - 'dependencies.yaml' - - '!**/*/REVIEW_GUIDELINES.md' - - '!cpp/doxygen/**' - test_java: - - '**' - - '!**/*/REVIEW_GUIDELINES.md' - - '!.coderabbit.yaml' - - '!.devcontainer/**' - - '!.github/CODEOWNERS' - - '!.github/ISSUE_TEMPLATE/**' - - '!.github/PULL_REQUEST_TEMPLATE.md' - - '!.github/copy-pr-bot.yaml' - - '!.github/labeler.yml' - - '!.github/ops-bot.yaml' - - '!.github/release.yml' - - '!.github/workflows/build.yaml' - - '!.github/workflows/check-c-abi.yaml' - - '!.github/workflows/labeler.yml' - - '!.github/workflows/publish-rust.yaml' - - '!.github/workflows/security-suite.yml' - - '!.github/workflows/store-c-abi-baseline.yaml' - - '!.github/workflows/test.yaml' - - '!.github/workflows/trigger-breaking-change-alert.yaml' - - '!.github/workflows/update-c-abi-baseline.yaml' - - '!.pre-commit-config.yaml' - - '!.shellcheckrc' - - '!.yamllint.yaml' - - '!CODEOWNERS' - - '!Dockerfile' - - '!README.md' - - '!SECURITY.md' - - '!ci/build_docs.sh' - - '!ci/build_go.sh' - - '!ci/build_python.sh' - - '!ci/build_rust.sh' - - '!ci/build_wheel*.sh' - - '!ci/checkstyle.sh' - - '!ci/run_cuvs_pytests.sh' - - '!ci/test_cpp.sh' - - '!ci/test_python.sh' - - '!ci/test_wheel_cuvs.sh' - - '!ci/validate_wheel.sh' - - '!conda/environments/**' - - '!conda/recipes/cuvs/**' - - '!conda/recipes/cuvs-bench/**' - - '!conda/recipes/cuvs-bench-cpu/**' - - '!docs/**' - - '!examples/.clang-format' - - '!examples/.clang-tidy' - - '!examples/.clangd' - - '!examples/README.md' - - '!examples/build.sh' - - '!examples/c/**' - - '!examples/cmake/**' - - '!examples/cpp/**' - - '!examples/go/**' - - '!fern/**' - - '!go/**' - - '!img/**' - - '!notebooks/**' - - '!pyproject.toml' - - '!python/**' - - '!rust/**' - - '!thirdparty/LICENSES/**' - test_python_conda: - - '**' - - '!**/*/REVIEW_GUIDELINES.md' - - '!.coderabbit.yaml' - - '!.devcontainer/**' - - '!.github/CODEOWNERS' - - '!.github/ISSUE_TEMPLATE/**' - - '!.github/PULL_REQUEST_TEMPLATE.md' - - '!.github/copy-pr-bot.yaml' - - '!.github/labeler.yml' - - '!.github/ops-bot.yaml' - - '!.github/release.yml' - - '!.github/workflows/build.yaml' - - '!.github/workflows/check-c-abi.yaml' - - '!.github/workflows/labeler.yml' - - '!.github/workflows/publish-rust.yaml' - - '!.github/workflows/security-suite.yml' - - '!.github/workflows/store-c-abi-baseline.yaml' - - '!.github/workflows/test.yaml' - - '!.github/workflows/trigger-breaking-change-alert.yaml' - - '!.github/workflows/update-c-abi-baseline.yaml' - - '!.pre-commit-config.yaml' - - '!.shellcheckrc' - - '!.yamllint.yaml' - - '!ci/build_go.sh' - - '!ci/build_java.sh' - - '!ci/build_lucene.sh' - - '!ci/build_rust.sh' - - '!ci/check_style.sh' - - '!ci/release/update-version.sh' - - '!ci/run_ctests.sh' - - '!ci/test_cpp.sh' - - '!ci/test_java.sh' - - '!ci/test_lucene.sh' - - '!ci/test_standalone_c.sh' - - '!ci/test_wheel_cuvs.sh' - - '!ci/validate_wheel.sh' - - '!cpp/.clang-format' - - '!cpp/.clang-tidy' - - '!CODEOWNERS' - - '!Dockerfile' - - '!README.md' - - '!SECURITY.md' - - '!docs/**' - - '!examples/**' - - '!fern/**' - - '!go/**' - - '!img/**' - - '!java/**' - - '!notebooks/**' - - '!rust/**' - - '!thirdparty/LICENSES/**' - test_python_wheels: - - '**' - - '!**/*/REVIEW_GUIDELINES.md' - - '!.coderabbit.yaml' - - '!.devcontainer/**' - - '!.github/CODEOWNERS' - - '!.github/ISSUE_TEMPLATE/**' - - '!.github/PULL_REQUEST_TEMPLATE.md' - - '!.github/copy-pr-bot.yaml' - - '!.github/labeler.yml' - - '!.github/ops-bot.yaml' - - '!.github/release.yml' - - '!.github/workflows/build.yaml' - - '!.github/workflows/check-c-abi.yaml' - - '!.github/workflows/labeler.yml' - - '!.github/workflows/publish-rust.yaml' - - '!.github/workflows/security-suite.yml' - - '!.github/workflows/store-c-abi-baseline.yaml' - - '!.github/workflows/test.yaml' - - '!.github/workflows/trigger-breaking-change-alert.yaml' - - '!.github/workflows/update-c-abi-baseline.yaml' - - '!.pre-commit-config.yaml' - - '!.shellcheckrc' - - '!.yamllint.yaml' - - '!ci/build_cpp.sh' - - '!ci/build_docs.sh' - - '!ci/build_go.sh' - - '!ci/build_java.sh' - - '!ci/build_lucene.sh' - - '!ci/build_python.sh' - - '!ci/build_rust.sh' - - '!ci/build_standalone_c.sh' - - '!ci/check_style.sh' - - '!ci/release/update-version.sh' - - '!ci/run_ctests.sh' - - '!ci/test_cpp.sh' - - '!ci/test_java.sh' - - '!ci/test_lucene.sh' - - '!ci/test_python.sh' - - '!ci/test_standalone_c.sh' - - '!ci/validate_wheel.sh' - - '!CODEOWNERS' - - '!Dockerfile' - - '!README.md' - - '!SECURITY.md' - - '!conda/**' - - '!cpp/.clang-format' - - '!cpp/.clang-tidy' - - '!docs/**' - - '!examples/**' - - '!fern/**' - - '!java/**' - - '!go/**' - - '!img/**' - - '!notebooks/**' - - '!rust/**' - - '!thirdparty/LICENSES/**' - test_rust: - - '**' - - '!**/*/REVIEW_GUIDELINES.md' - - '!.coderabbit.yaml' - - '!.devcontainer/**' - - '!.github/CODEOWNERS' - - '!.github/ISSUE_TEMPLATE/**' - - '!.github/PULL_REQUEST_TEMPLATE.md' - - '!.github/copy-pr-bot.yaml' - - '!.github/labeler.yml' - - '!.github/ops-bot.yaml' - - '!.github/release.yml' - - '!.github/workflows/build.yaml' - - '!.github/workflows/check-c-abi.yaml' - - '!.github/workflows/labeler.yml' - - '!.github/workflows/publish-rust.yaml' - - '!.github/workflows/security-suite.yml' - - '!.github/workflows/store-c-abi-baseline.yaml' - - '!.github/workflows/test.yaml' - - '!.github/workflows/trigger-breaking-change-alert.yaml' - - '!.github/workflows/update-c-abi-baseline.yaml' - - '!.pre-commit-config.yaml' - - '!.shellcheckrc' - - '!.yamllint.yaml' - - '!CODEOWNERS' - - '!Dockerfile' - - '!README.md' - - '!SECURITY.md' - - '!ci/build_docs.sh' - - '!ci/build_go.sh' - - '!ci/build_java.sh' - - '!ci/build_lucene.sh' - - '!ci/build_python.sh' - - '!ci/build_wheel*.sh' - - '!ci/check_style.sh' - - '!ci/release/update-version.sh' - - '!ci/run_ctests.sh' - - '!ci/run_cuvs_pytests.sh' - - '!ci/test_cpp.sh' - - '!ci/test_java.sh' - - '!ci/test_lucene.sh' - - '!ci/test_python.sh' - - '!ci/test_wheel_cuvs.sh' - - '!ci/validate_wheel.sh' - - '!docs/**' - - '!examples/**' - - '!fern/**' - - '!go/**' - - '!img/**' - - '!java/**' - - '!notebooks/**' - - '!python/**' - - '!thirdparty/LICENSES/**' - test_go: - - '**' - - '!**/*/REVIEW_GUIDELINES.md' - - '!.coderabbit.yaml' - - '!.devcontainer/**' - - '!.github/CODEOWNERS' - - '!.github/ISSUE_TEMPLATE/**' - - '!.github/PULL_REQUEST_TEMPLATE.md' - - '!.github/copy-pr-bot.yaml' - - '!.github/labeler.yml' - - '!.github/ops-bot.yaml' - - '!.github/release.yml' - - '!.github/workflows/build.yaml' - - '!.github/workflows/check-c-abi.yaml' - - '!.github/workflows/labeler.yml' - - '!.github/workflows/publish-rust.yaml' - - '!.github/workflows/security-suite.yml' - - '!.github/workflows/store-c-abi-baseline.yaml' - - '!.github/workflows/test.yaml' - - '!.github/workflows/trigger-breaking-change-alert.yaml' - - '!.github/workflows/update-c-abi-baseline.yaml' - - '!.pre-commit-config.yaml' - - '!.shellcheckrc' - - '!.yamllint.yaml' - - '!CODEOWNERS' - - '!Dockerfile' - - '!README.md' - - '!SECURITY.md' - - '!ci/build_docs.sh' - - '!ci/build_java.sh' - - '!ci/build_lucene.sh' - - '!ci/build_python.sh' - - '!ci/build_rust.sh' - - '!ci/build_wheel*.sh' - - '!ci/check_style.sh' - - '!ci/release/update-version.sh' - - '!ci/run_ctests.sh' - - '!ci/run_cuvs_pytests.sh' - - '!ci/test_cpp.sh' - - '!ci/test_java.sh' - - '!ci/test_lucene.sh' - - '!ci/test_python.sh' - - '!ci/test_standalone_c.sh' - - '!ci/test_wheel_cuvs.sh' - - '!ci/validate_wheel.sh' - - '!docs/**' - - '!examples/.clang-format' - - '!examples/.clang-tidy' - - '!examples/.clangd' - - '!examples/README.md' - - '!examples/build.sh' - - '!examples/c/**' - - '!examples/cmake/**' - - '!examples/cpp/**' - - '!examples/java/**' - - '!fern/**' - - '!img/**' - - '!java/**' - - '!notebooks/**' - - '!python/**' - - '!rust/**' - - '!thirdparty/LICENSES/**' - checks: - needs: telemetry-setup - uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@main - permissions: - contents: read - with: - enable_check_generated_files: false - ignored_pr_jobs: "telemetry-summarize" - check-c-abi: - needs: telemetry-setup - permissions: - actions: read - contents: read - issues: write - pull-requests: read - uses: ./.github/workflows/check-c-abi.yaml - security-suite: - permissions: - actions: read - contents: read - id-token: write # OIDC -> Vault -> nvcr.io image pull - security-events: write # publish redacted SARIF to code scanning - uses: ./.github/workflows/security-suite.yml conda-cpp-build: - needs: [build-details, checks] + needs: [build-details] permissions: actions: read contents: read @@ -468,126 +50,9 @@ jobs: build-datetime: ${{ needs.build-details.outputs.build-datetime }} node_type: cpu16 script: ci/build_cpp.sh - conda-cpp-tests: - needs: [conda-cpp-build, changed-files] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - secrets: inherit # zizmor: ignore[secrets-inherit] - uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@main - if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp - # Split the C++ tests across several jobs for each CUDA/GPU configuration - strategy: - fail-fast: false - matrix: - shard: [1, 2, 3, 4] - num_shards: [4] - with: - build_type: pull-request - script: "ci/test_cpp.sh ${{ matrix.shard }} ${{ matrix.num_shards }}" - cache-paths: .cache/cuda-jit - cache-key-prefix: libcuvs-cudajit-v1 - # Caches are immutable. Re-key when the JIT-relevant sources change so a completed run can - # save newly compiled entries; restore a compatible earlier archive first. - cache-key-files: | - cpp/CMakeLists.txt - cpp/cmake/** - cpp/include/cuvs/detail/jit_lto/** - cpp/src/detail/jit_lto/** - cpp/src/distance/detail/pairwise_matrix/jit_lto_kernels/** - cpp/src/neighbors/detail/cagra/jit_lto_kernels/** - cpp/src/neighbors/detail/jit_lto_kernels/** - cpp/src/neighbors/ivf_flat/detail/jit_lto_kernels/** - cpp/src/neighbors/ivf_pq/detail/jit_lto_kernels/** - cpp/src/neighbors/ivf_rabitq/jit_lto_kernels/** - cpp/src/neighbors/ivf_sq/detail/jit_lto_kernels/** - cpp/tests/** - dependencies.yaml - # The CUDA driver's JIT cache is independent of the Python interpreter used to run C++ tests. - cache-key-matrix-fields: | - CUDA_VER - ARCH - LINUX_VER - GPU - DRIVER - DEPENDENCIES - # PRs restore a compatible main cache but do not create merge-ref-scoped entries. - cache-read-only: true - cache-environment: | - CUDA_CACHE_PATH=.cache/cuda-jit - conda-cpp-checks: - needs: conda-cpp-build - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-post-build-checks.yaml@main - with: - build_type: pull-request - symbol_exclusions: (void (thrust::|cub::)) - package_name: libcuvs - conda-python-build: - needs: [build-details, conda-cpp-build] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - secrets: inherit # zizmor: ignore[secrets-inherit] - uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@main - with: - build_type: pull-request - build-datetime: ${{ needs.build-details.outputs.build-datetime }} - script: ci/build_python.sh - # Build a conda package for each CUDA x ARCH x minimum supported Python version - matrix_filter: group_by({CUDA_VER, ARCH}) | map(min_by(.PY_VER | split(".") | map(tonumber))) - conda-python-tests: - needs: [conda-python-build, changed-files] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - secrets: inherit # zizmor: ignore[secrets-inherit] - uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@main - if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_conda - with: - build_type: pull-request - script: ci/test_python.sh - rocky8-clib-standalone-build: - needs: checks - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - uses: ./.github/workflows/clib-build.yaml - with: - build_type: pull-request - rocky8-clib-tests: - needs: [rocky8-clib-standalone-build, changed-files] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - uses: ./.github/workflows/matrix-job.yaml - if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_standalone_c - with: - build_type: pull-request - container-image-repository: "rapidsai/citestwheel" - script: "ci/test_standalone_c.sh" + matrix_filter: map(select(.CUDA_VER | startswith("13"))) conda-java-build: - needs: [conda-cpp-build, changed-files] + needs: [conda-cpp-build] permissions: actions: read contents: read @@ -595,28 +60,15 @@ jobs: packages: read pull-requests: read uses: ./.github/workflows/matrix-job.yaml - if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_java || fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp # Artifacts are not published from these jobs, so it's safe to run for multiple CUDA versions. # If these jobs start producing artifacts, the names will have to differentiate between CUDA versions. with: build_type: pull-request node_type: cpu8 - matrix_filter: map(select(.ARCH == "amd64")) + matrix_filter: map(select(.ARCH == "amd64" and (.CUDA_VER | startswith("13")))) script: "ci/build_java.sh" artifact-name-prefix: "cuvs-java" file_to_upload: "java/cuvs-java/target/" - conda-java-tests: - needs: conda-java-build - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - uses: ./.github/workflows/matrix-job.yaml - with: - build_type: pull-request - script: "ci/test_java.sh" conda-lucene-build: # Depends on the Java job for the cuvs-java artifact that cuvs-lucene builds against. needs: conda-java-build @@ -632,7 +84,7 @@ jobs: with: build_type: pull-request node_type: cpu8 - matrix_filter: map(select(.ARCH == "amd64")) + matrix_filter: map(select(.ARCH == "amd64" and (.CUDA_VER | startswith("13")))) script: "ci/build_lucene.sh" artifact-name-prefix: "cuvs-lucene" file_to_upload: "java/cuvs-lucene/target/" @@ -647,150 +99,5 @@ jobs: uses: ./.github/workflows/matrix-job.yaml with: build_type: pull-request + matrix_filter: 'map(select(.CUDA_VER | startswith("13"))) | [range(2) as $i | .[] | . + {COPY: $i}]' script: "ci/test_lucene.sh" - rust-build: - needs: [conda-cpp-build, changed-files] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - uses: ./.github/workflows/matrix-job.yaml - if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_rust || fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp - # Artifacts are not published from these jobs, so it's safe to run for multiple CUDA versions. - # If these jobs start producing artifacts, the names will have to differentiate between CUDA versions. - with: - build_type: pull-request - matrix_filter: map(select(.ARCH == "amd64")) - script: "ci/build_rust.sh" - go-build: - needs: [conda-cpp-build, changed-files] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - uses: ./.github/workflows/matrix-job.yaml - if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_go || fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp - # Artifacts are not published from these jobs, so it's safe to run for multiple CUDA versions. - # If these jobs start producing artifacts, the names will have to differentiate between CUDA versions. - with: - build_type: pull-request - matrix_filter: map(select(.ARCH == "amd64")) - script: "ci/build_go.sh" - docs-build-matrix: - permissions: - contents: read - uses: rapidsai/shared-workflows/.github/workflows/compute-matrix.yaml@main - with: - build_type: pull-request - matrix_name: docs-build - docs-build: - needs: [docs-build-matrix] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - secrets: inherit # zizmor: ignore[secrets-inherit] - uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main - if: fromJSON(needs.changed-files.outputs.changed_file_groups).build_docs - strategy: - fail-fast: false - matrix: ${{ fromJSON(needs.docs-build-matrix.outputs.matrix) }} - with: - build_type: pull-request - node_type: "gpu-${{ matrix.GPU }}-${{ matrix.DRIVER }}-1" - arch: "${{ matrix.arch }}" - container_image: "rapidsai/ci-conda:26.12-cuda${{ matrix.CUDA_VER }}-${{ matrix.LINUX_VER }}-py${{ matrix.PY_VER }}" - script: "ci/build_docs.sh" - wheel-build-libcuvs: - needs: [build-details, checks] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - secrets: inherit # zizmor: ignore[secrets-inherit] - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main - with: - build_type: pull-request - build-datetime: ${{ needs.build-details.outputs.build-datetime }} - node_type: cpu16 - script: ci/build_wheel_libcuvs.sh - # build for every combination of arch and CUDA version, but only for the latest Python - matrix_filter: group_by([.ARCH, (.CUDA_VER|split(".")|map(tonumber)|.[0])]) | map(max_by(.PY_VER|split(".")|map(tonumber))) - package-name: libcuvs - package-type: cpp - wheel-build-cuvs: - needs: [build-details, wheel-build-libcuvs] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - secrets: inherit # zizmor: ignore[secrets-inherit] - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main - with: - build_type: pull-request - build-datetime: ${{ needs.build-details.outputs.build-datetime }} - node_type: cpu8 - script: ci/build_wheel_cuvs.sh - package-name: cuvs - package-type: python - # Build a wheel for each CUDA x ARCH x minimum supported Python version - matrix_filter: group_by({CUDA_VER, ARCH}) | map(min_by(.PY_VER | split(".") | map(tonumber))) - wheel-tests-cuvs: - needs: [wheel-build-cuvs, changed-files] - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - secrets: inherit # zizmor: ignore[secrets-inherit] - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@main - if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_wheels - with: - build_type: pull-request - script: ci/test_wheel_cuvs.sh - devcontainer: - permissions: - actions: read - contents: read - id-token: write - packages: read - pull-requests: read - secrets: inherit # zizmor: ignore[secrets-inherit] - needs: telemetry-setup - uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@main - with: - arch: '["amd64", "arm64"]' - cuda: '["13.3"]' - node_type: "cpu8" - env: | - SCCACHE_DIST_MAX_RETRIES=inf - SCCACHE_SERVER_LOG=sccache=debug - SCCACHE_DIST_FALLBACK_TO_LOCAL_COMPILE=false - build_command: | - sccache --zero-stats; - build-all -j0 --verbose 2>&1 | tee telemetry-artifacts/build.log; - sccache --show-adv-stats | tee telemetry-artifacts/sccache-stats.txt; - telemetry-summarize: - # This job must use a self-hosted runner to record telemetry traces. - runs-on: linux-amd64-cpu4 - needs: pr-builder - permissions: - actions: write - if: ${{ vars.TELEMETRY_ENABLED == 'true' && !cancelled() }} - continue-on-error: true - steps: - - name: Telemetry summarize - uses: rapidsai/shared-actions/telemetry-dispatch-summarize@main - env: - GH_TOKEN: ${{ github.token }} diff --git a/ci/test_lucene.sh b/ci/test_lucene.sh index 94d3d591fb..786e640b5a 100755 --- a/ci/test_lucene.sh +++ b/ci/test_lucene.sh @@ -95,7 +95,31 @@ rapids-logger "Run cuvs-lucene tests against the amd64-built classes" # compilation for this run, forcing test to use amd64-compiled jar instead of # local code. pushd java/cuvs-lucene -mvn --batch-mode test -Dskip.compile=true +MVN_TEST=(mvn --batch-mode test -Dskip.compile=true -DfailIfNoTests=false -Dsurefire.failIfNoSpecifiedTests=false) + +rapids-logger "DIAG: shortfall rate of filtered multi-partition search" +"${MVN_TEST[@]}" -Dtest=TestDiagFilteredMultiPartitionShortfall -Ddiag.trials=6 2>&1 | grep -E "DIAG|FAIL|ERROR|Tests run" || true + +rapids-logger "DIAG: failing CI seeds" +for seed in 4A2D37AD61DD7303 C28C44A945519487 653B80E5E4427103 DDB057FC1538C8F3 ED8335DC8AEF8040 EE3B5EEAC86DF8C; do + for rep in 1 2 3; do + echo "SEEDRUN TestMultiSegmentGPUFilterConcurrency seed=${seed} rep=${rep}" + "${MVN_TEST[@]}" -Dtest='TestMultiSegmentGPUFilterConcurrency#filteredSearchesTakeTheMultiPartitionGpuPath' -Dtests.seed="${seed}" 2>&1 | grep -E "AssertionError|Tests run:|FAIL" | head -5 || true + done +done +echo "SEEDRUN TestCuVSGaps seed=BBE2A1CCC7772A7F" +"${MVN_TEST[@]}" -Dtest='TestCuVSGaps' -Dtests.seed=BBE2A1CCC7772A7F 2>&1 | grep -E "AssertionError|Tests run:|FAIL" | head -5 || true + +rapids-logger "DIAG: repeated random-seed runs" +fails=0 +for i in $(seq 1 40); do + out=$("${MVN_TEST[@]}" -Dtest='TestMultiSegmentGPUFilterConcurrency#filteredSearchesTakeTheMultiPartitionGpuPath' 2>&1) + if echo "${out}" | grep -q "AssertionError"; then + fails=$((fails+1)) + echo "LOOP iter=${i} FAILED: $(echo "${out}" | grep -E 'AssertionError|reproduce with' | head -2 | tr '\n' ' ')" + fi +done +echo "LOOP TestMultiSegmentGPUFilterConcurrency failures=${fails}/40" popd rapids-logger "Test script exiting with value: $EXITCODE" diff --git a/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestDiagFilteredMultiPartitionShortfall.java b/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestDiagFilteredMultiPartitionShortfall.java new file mode 100644 index 0000000000..94b2f215d2 --- /dev/null +++ b/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestDiagFilteredMultiPartitionShortfall.java @@ -0,0 +1,150 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.nvidia.cuvs.lucene; + +import static com.nvidia.cuvs.lucene.TestUtils.generateDataset; +import static com.nvidia.cuvs.lucene.ThreadLocalCuVSResourcesProvider.isSupported; +import static org.apache.lucene.index.VectorSimilarityFunction.EUCLIDEAN; + +import com.nvidia.cuvs.CagraSearchParams.SearchAlgo; +import com.nvidia.cuvs.spi.CuVSProvider; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; +import org.apache.lucene.codecs.Codec; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.KnnFloatVectorField; +import org.apache.lucene.document.StringField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.NoMergePolicy; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.store.ByteBuffersDirectory; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.util.LuceneTestCase; +import org.apache.lucene.tests.util.LuceneTestCase.SuppressSysoutChecks; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Diagnostic (not a regression test): measures how often a filtered multi-partition GPU search + * returns fewer than k hits (and therefore falls back to the per-segment path) for the same index + * shape as {@link TestMultiSegmentGPUFilterConcurrency}, across several CAGRA itopk/algo settings. + */ +@SuppressSysoutChecks(bugUrl = "") +public class TestDiagFilteredMultiPartitionShortfall extends LuceneTestCase { + + private static final String VECTOR_FIELD = "vectors"; + private static final String CATEGORY_FIELD = "cat"; + + @BeforeClass + public static void beforeClass() { + try { + CuVSProvider.provider().enableRMMAsyncMemory(); + } catch (UnsupportedOperationException unsupported) { + assumeTrue("cuVS not supported: " + unsupported.getMessage(), false); + } + assumeTrue("cuVS not supported", isSupported()); + } + + private record Config(int iTopK, SearchAlgo algo) {} + + @Test + public void measureShortfallRate() throws Exception { + int trials = Integer.getInteger("diag.trials", 6); + int numQueries = Integer.getInteger("diag.queries", 64); + int[] numCategoriesOptions = {24, 48}; + final int datasetSize = 2000; + final int dimensions = 128; + final int topK = 10; + + Config[] configs = { + new Config(10, SearchAlgo.AUTO), + new Config(10, SearchAlgo.MULTI_CTA), + new Config(10, SearchAlgo.SINGLE_CTA), + new Config(32, SearchAlgo.AUTO), + new Config(64, SearchAlgo.AUTO), + new Config(128, SearchAlgo.AUTO), + new Config(256, SearchAlgo.AUTO), + }; + + Codec codec = new CuVS2510GPUSearchCodec(); + Random rnd = random(); + for (int numCategories : numCategoriesOptions) { + Map totals = new LinkedHashMap<>(); + for (Config c : configs) totals.put(c, new long[2]); + for (int trial = 0; trial < trials; trial++) { + try (Directory directory = new ByteBuffersDirectory()) { + IndexWriterConfig config = + new IndexWriterConfig().setCodec(codec).setMergePolicy(NoMergePolicy.INSTANCE); + float[][] dataset = generateDataset(rnd, datasetSize, dimensions); + try (IndexWriter writer = new IndexWriter(directory, config)) { + final int commitEvery = datasetSize / 4; + for (int i = 0; i < datasetSize; i++) { + Document doc = new Document(); + doc.add(new StringField("id", String.valueOf(i), Field.Store.YES)); + doc.add(new StringField(CATEGORY_FIELD, "c" + (i % numCategories), Field.Store.NO)); + doc.add(new KnnFloatVectorField(VECTOR_FIELD, dataset[i], EUCLIDEAN)); + writer.addDocument(doc); + if ((i + 1) % commitEvery == 0) { + writer.commit(); + } + } + writer.commit(); + } + try (DirectoryReader reader = DirectoryReader.open(directory)) { + IndexSearcher searcher = new IndexSearcher(reader); + float[][] queries = generateDataset(rnd, numQueries, dimensions); + for (Config cfg : configs) { + long[] t = totals.get(cfg); + long trialFallbacks = 0; + for (int c = 0; c < numCategories; c++) { + Query filter = new TermQuery(new Term(CATEGORY_FIELD, "c" + c)); + for (float[] q : queries) { + GPUKnnFloatVectorQuery query = + new GPUKnnFloatVectorQuery( + VECTOR_FIELD, q, topK, filter, cfg.iTopK(), 1, 0, 0, cfg.algo()); + boolean gpu = searcher.rewrite(query).toString().contains("GPUDocAndScoreQuery"); + t[0]++; + if (!gpu) { + t[1]++; + trialFallbacks++; + } + } + } + System.out.println( + "DIAG trial=" + + trial + + " categories=" + + numCategories + + " itopk=" + + cfg.iTopK() + + " algo=" + + cfg.algo() + + " shortfalls=" + + trialFallbacks + + "/" + + (numCategories * queries.length) + + " anyShortfallPer24Filters=" + + (trialFallbacks > 0)); + } + } + } + } + for (Map.Entry e : totals.entrySet()) { + long[] t = e.getValue(); + System.out.printf( + "DIAG-SUMMARY categories=%d itopk=%d algo=%s shortfalls=%d/%d (%.4f%%)%n", + numCategories, e.getKey().iTopK(), e.getKey().algo(), t[1], t[0], 100.0 * t[1] / t[0]); + } + } + } +} From 67783e058cb31b06e34da2e5de04f0dcaa7d72dd Mon Sep 17 00:00:00 2001 From: Bradley Dice Date: Sat, 10 Oct 2026 16:00:35 +0000 Subject: [PATCH 2/3] Derive the filtering rate in filtered multi-partition CAGRA search Single-partition CAGRA search computes the filtering rate from the filter bitset when the caller does not set one, and MULTI_CTA enlarges itopk_size for selective filters accordingly. The multi-partition search skipped this, so a selective per-partition filter searched with the unadjusted itopk_size and could return fewer than k neighbors although every partition held more than k rows passing the filter. cuvs-lucene then routes the query back to per-segment search, which made TestMultiSegmentGPUFilterConcurrency flaky. Derive the rate from the per-partition bitsets, sizing the shared plan for the most selective partition that accepts any row. Also limit topK in TestCuVSGaps.testVectorSearchWithAlternatingDocuments to the number of documents that have vectors. --- cpp/src/neighbors/cagra.cuh | 30 +++- .../ann_cagra/test_float_uint32_t.cu | 148 ++++++++++++++++++ .../com/nvidia/cuvs/lucene/TestCuVSGaps.java | 4 +- 3 files changed, 180 insertions(+), 2 deletions(-) diff --git a/cpp/src/neighbors/cagra.cuh b/cpp/src/neighbors/cagra.cuh index 0e1454d987..c781d1e427 100644 --- a/cpp/src/neighbors/cagra.cuh +++ b/cpp/src/neighbors/cagra.cuh @@ -606,10 +606,38 @@ void search( cuvs::neighbors::filtering::none_sample_filter>( res, params, indices, queries, partition_ids, neighbors, distances, partition_bitsets); } else { + // As in the single-partition bitset path, derive the filtering rate from the bitsets when the + // caller did not set one, so that the search plan enlarges itopk_size for selective filters. + // Without this, a selective filter leaves too few valid candidates and the search can return + // fewer than k neighbors. One plan serves every partition, so size it for the most selective + // partition that still accepts at least one row; a partition that accepts nothing contributes + // no neighbors whatever the itopk_size, and an unfiltered partition has a rate of zero. + search_params params_copy = params; + if (params.filtering_rate < 0.0) { + float filtering_rate = 0.0f; + for (size_t i = 0; i < partition_bitsets.size() && i < indices.size(); i++) { + const auto& v = partition_bitsets[i]; + if (v.data() == nullptr || v.size() == 0) { continue; } + const int64_t n_rows = indices[i]->dataset().n_rows(); + if (n_rows == 0) { continue; } + // Count only this partition's rows; the bitset may be padded past n_rows. + const auto num_set_bits = + cuvs::core::bitset_view(const_cast(v.data()), + std::min(n_rows, v.size())) + .count(res); + if (num_set_bits == 0) { continue; } + filtering_rate = + std::max(filtering_rate, static_cast(n_rows - num_set_bits) / n_rows); + } + const float min_filtering_rate = 0.0; + const float max_filtering_rate = 0.999; + params_copy.filtering_rate = + std::min(std::max(filtering_rate, min_filtering_rate), max_filtering_rate); + } using bitset_filter_t = cuvs::neighbors::filtering::bitset_filter; cagra::detail::search_multi_partition( res, - params, + params_copy, indices, queries, partition_ids, diff --git a/cpp/tests/neighbors/ann_cagra/test_float_uint32_t.cu b/cpp/tests/neighbors/ann_cagra/test_float_uint32_t.cu index 5dccce21da..2b445df7bc 100644 --- a/cpp/tests/neighbors/ann_cagra/test_float_uint32_t.cu +++ b/cpp/tests/neighbors/ann_cagra/test_float_uint32_t.cu @@ -7,6 +7,8 @@ #include "../ann_cagra.cuh" +#include +#include #include #include @@ -135,6 +137,152 @@ TEST(AnnCagraMultiPartition, MultiKernelRejected) search_params); } +namespace { +// Runs a filtered multi-partition MULTI_CTA search where every partition keeps only ~4% of its rows +// (still more than k each) and reports the number of queries that returned fewer than k neighbors +// and the recall against an exact search over the kept rows. +void search_with_selective_filter(float filtering_rate, int& short_queries, double& recall) +{ + raft::resources handle; + auto stream = raft::resource::get_cuda_stream(handle); + + constexpr int num_partitions = 4, part_size = 500, dim = 128, n_queries = 500, k = 10; + // Keep one row in `keep_every` (~4% of each partition, i.e. ~21 rows per partition). + constexpr int keep_every = 24; + constexpr int n_rows = num_partitions * part_size; + + rmm::device_uvector database(static_cast(n_rows) * dim, stream); + rmm::device_uvector queries(static_cast(n_queries) * dim, stream); + raft::random::RngState r(1234ULL); + InitDataset(handle, database.data(), n_rows, dim, cuvs::distance::DistanceType::L2Expanded, r); + InitDataset(handle, queries.data(), n_queries, dim, cuvs::distance::DistanceType::L2Expanded, r); + raft::resource::sync_stream(handle); + + cagra::index_params index_params; + index_params.graph_degree = 32; + index_params.intermediate_graph_degree = 64; + index_params.graph_build_params = graph_build_params::nn_descent_params( + index_params.intermediate_graph_degree, index_params.metric); + + std::vector> part_indices; + // An index only holds a view, so any padded copy has to outlive it. + std::vector> part_padded; + part_padded.reserve(num_partitions); + std::vector> part_bitsets; + part_bitsets.reserve(num_partitions); + std::vector> partition_bitsets; + std::vector removed_local; + for (int64_t i = 0; i < part_size; i++) { + if (i % keep_every != 0) { removed_local.push_back(i); } + } + auto removed = + raft::make_device_vector(handle, static_cast(removed_local.size())); + raft::update_device(removed.data_handle(), removed_local.data(), removed_local.size(), stream); + for (int p = 0; p < num_partitions; p++) { + auto view = raft::make_device_matrix_view( + database.data() + static_cast(p) * part_size * dim, part_size, dim); + part_padded.emplace_back(handle, view); + auto const& padded = part_padded.back().view; + part_indices.push_back(cagra::build(handle, index_params, padded)); + auto& part_index = part_indices.back(); + part_index = cagra::update_dataset(handle, std::move(part_index), padded); + part_bitsets.emplace_back(handle, removed.view(), part_size); + partition_bitsets.push_back(part_bitsets.back().view()); + } + std::vector*> index_ptrs; + for (auto& idx : part_indices) { + index_ptrs.push_back(&idx); + } + + const size_t out_size = static_cast(n_queries) * k; + rmm::device_uvector partition_ids_dev(out_size, stream); + rmm::device_uvector neighbors_dev(out_size, stream); + rmm::device_uvector distances_dev(out_size, stream); + + // A small itopk_size, as used for small k, on the MULTI_CTA algorithm that AUTO picks for a + // handful of queries over a handful of partitions. + cagra::search_params search_params; + search_params.algo = search_algo::MULTI_CTA; + search_params.itopk_size = k; + search_params.filtering_rate = filtering_rate; + + cagra::search( + handle, + search_params, + index_ptrs, + raft::make_device_matrix_view(queries.data(), n_queries, dim), + raft::make_device_matrix_view(partition_ids_dev.data(), n_queries, k), + raft::make_device_matrix_view(neighbors_dev.data(), n_queries, k), + raft::make_device_matrix_view(distances_dev.data(), n_queries, k), + partition_bitsets); + + std::vector partition_ids(out_size); + std::vector neighbors(out_size); + std::vector distances(out_size); + std::vector host_database(static_cast(n_rows) * dim); + std::vector host_queries(static_cast(n_queries) * dim); + raft::update_host(partition_ids.data(), partition_ids_dev.data(), out_size, stream); + raft::update_host(neighbors.data(), neighbors_dev.data(), out_size, stream); + raft::update_host(distances.data(), distances_dev.data(), out_size, stream); + raft::update_host(host_database.data(), database.data(), host_database.size(), stream); + raft::update_host(host_queries.data(), queries.data(), host_queries.size(), stream); + raft::resource::sync_stream(handle); + + // Exact top-k over the rows that pass the filter, for recall. + std::vector kept_rows; + for (int64_t g = 0; g < n_rows; g++) { + if ((g % part_size) % keep_every == 0) { kept_rows.push_back(g); } + } + ASSERT_GT(kept_rows.size(), static_cast(k)); + + short_queries = 0; + size_t matches = 0; + for (int q = 0; q < n_queries; q++) { + std::vector> exact; + for (auto g : kept_rows) { + float d = 0; + for (int j = 0; j < dim; j++) { + const float diff = host_queries[q * dim + j] - host_database[g * dim + j]; + d += diff * diff; + } + exact.emplace_back(d, g); + } + std::partial_sort(exact.begin(), exact.begin() + k, exact.end()); + std::vector expected; + for (int j = 0; j < k; j++) { + expected.push_back(exact[j].second); + } + + int valid = 0; + for (int j = 0; j < k; j++) { + const size_t i = static_cast(q) * k + j; + if (distances[i] == std::numeric_limits::max()) { continue; } + ASSERT_LT(partition_ids[i], static_cast(num_partitions)); + ASSERT_LT(neighbors[i], static_cast(part_size)); + ASSERT_EQ(neighbors[i] % keep_every, 0u) << "filtered-out row returned"; + valid++; + const int64_t g = static_cast(partition_ids[i]) * part_size + neighbors[i]; + if (std::find(expected.begin(), expected.end(), g) != expected.end()) { matches++; } + } + if (valid < k) { short_queries++; } + } + recall = static_cast(matches) / out_size; +} +} // namespace + +// A selective per-partition filter must not leave the search short of neighbors. Single-partition +// search derives the filtering rate from the bitset and enlarges itopk_size accordingly; the +// multi-partition search must do the same, or MULTI_CTA can return fewer than k valid neighbors +// even though every partition holds more than k rows that pass the filter. +TEST(AnnCagraMultiPartition, SelectiveFilterReturnsKNeighbors) +{ + int short_queries = 0; + double recall = 0; + search_with_selective_filter(-1.0f, short_queries, recall); + EXPECT_EQ(short_queries, 0) << "queries returning fewer than k neighbors"; + EXPECT_GE(recall, 0.95); +} + // The shared plan descriptor and the cross-partition select_k direction are derived from // indices[0], so all partitions must share one metric; a mismatch must be rejected. TEST(AnnCagraMultiPartition, MixedMetricRejected) diff --git a/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestCuVSGaps.java b/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestCuVSGaps.java index 31958ef0db..e9bdfea9aa 100644 --- a/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestCuVSGaps.java +++ b/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestCuVSGaps.java @@ -114,7 +114,9 @@ public void testVectorSearchWithAlternatingDocuments() throws IOException { // Use the first vector (from document 0) as query float[] queryVector = dataset[0]; - int topK = random.nextInt(5, TOP_K_LIMIT); + // Only even-numbered documents have vectors, so never ask for more results than exist. + int vectorCount = (datasetSize + 1) / 2; + int topK = Math.min(random.nextInt(5, TOP_K_LIMIT), vectorCount); GPUKnnFloatVectorQuery query = new GPUKnnFloatVectorQuery("vector", queryVector, topK, null, topK, 1); From 2d2bc59cc2a35cc24c00964fb30f4b36249f6081 Mon Sep 17 00:00:00 2001 From: Bradley Dice Date: Sat, 10 Oct 2026 16:01:27 +0000 Subject: [PATCH 3/3] [DO NOT MERGE] CI diagnostics for the multi-partition filtering-rate fix --- .github/workflows/pr.yaml | 15 +++++++++++++++ ci/test_cpp.sh | 2 +- ci/test_lucene.sh | 6 +++--- .../neighbors/ann_cagra/test_float_uint32_t.cu | 14 ++++++++++++++ .../TestDiagFilteredMultiPartitionShortfall.java | 2 +- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 0d0fd381b4..5ce6871cc0 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,6 +12,7 @@ jobs: needs: - build-details - conda-cpp-build + - conda-cpp-tests - conda-java-build - conda-lucene-build - conda-lucene-tests @@ -51,6 +52,20 @@ jobs: node_type: cpu16 script: ci/build_cpp.sh matrix_filter: map(select(.CUDA_VER | startswith("13"))) + conda-cpp-tests: + needs: [conda-cpp-build] + permissions: + actions: read + contents: read + id-token: write + packages: read + pull-requests: read + secrets: inherit # zizmor: ignore[secrets-inherit] + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@main + with: + build_type: pull-request + matrix_filter: map(select(.ARCH == "amd64" and (.CUDA_VER | startswith("13")))) | .[0:1] + script: "ci/test_cpp.sh" conda-java-build: needs: [conda-cpp-build] permissions: diff --git a/ci/test_cpp.sh b/ci/test_cpp.sh index 0e97a0ddef..9bf0bac094 100755 --- a/ci/test_cpp.sh +++ b/ci/test_cpp.sh @@ -67,7 +67,7 @@ fi # Run libcuvs gtests from libcuvs-tests package rapids-logger "Run libcuvs tests (shard ${SHARD} of ${NUM_SHARDS})" pushd "$CONDA_PREFIX"/bin/gtests/libcuvs -timeout -v --signal=SIGINT --kill-after=60s 100m ctest -j8 --output-on-failure -I "${SHARD},,${NUM_SHARDS}" +./NEIGHBORS_ANN_CAGRA_FLOAT_UINT32_TEST --gtest_filter='AnnCagraMultiPartition*' popd rapids-logger "Test script exiting with value: $EXITCODE" diff --git a/ci/test_lucene.sh b/ci/test_lucene.sh index 786e640b5a..80c152aabd 100755 --- a/ci/test_lucene.sh +++ b/ci/test_lucene.sh @@ -98,11 +98,11 @@ pushd java/cuvs-lucene MVN_TEST=(mvn --batch-mode test -Dskip.compile=true -DfailIfNoTests=false -Dsurefire.failIfNoSpecifiedTests=false) rapids-logger "DIAG: shortfall rate of filtered multi-partition search" -"${MVN_TEST[@]}" -Dtest=TestDiagFilteredMultiPartitionShortfall -Ddiag.trials=6 2>&1 | grep -E "DIAG|FAIL|ERROR|Tests run" || true +"${MVN_TEST[@]}" -Dtest=TestDiagFilteredMultiPartitionShortfall -Ddiag.trials=12 2>&1 | grep -E "DIAG|FAIL|ERROR|Tests run" || true rapids-logger "DIAG: failing CI seeds" -for seed in 4A2D37AD61DD7303 C28C44A945519487 653B80E5E4427103 DDB057FC1538C8F3 ED8335DC8AEF8040 EE3B5EEAC86DF8C; do - for rep in 1 2 3; do +for seed in 4A2D37AD61DD7303 C28C44A945519487 DDB057FC1538C8F3 EE3B5EEAC86DF8C; do + for rep in 1 2 3 4 5 6 7 8; do echo "SEEDRUN TestMultiSegmentGPUFilterConcurrency seed=${seed} rep=${rep}" "${MVN_TEST[@]}" -Dtest='TestMultiSegmentGPUFilterConcurrency#filteredSearchesTakeTheMultiPartitionGpuPath' -Dtests.seed="${seed}" 2>&1 | grep -E "AssertionError|Tests run:|FAIL" | head -5 || true done diff --git a/cpp/tests/neighbors/ann_cagra/test_float_uint32_t.cu b/cpp/tests/neighbors/ann_cagra/test_float_uint32_t.cu index 2b445df7bc..204e3c0fcc 100644 --- a/cpp/tests/neighbors/ann_cagra/test_float_uint32_t.cu +++ b/cpp/tests/neighbors/ann_cagra/test_float_uint32_t.cu @@ -301,4 +301,18 @@ TEST(AnnCagraMultiPartition, MixedGraphDegreeRejected) cagra::search_params{}); } +// [DO NOT MERGE] Diagnostic: an explicit rate of 0 reproduces the behavior before the fix. +TEST(AnnCagraMultiPartitionDiag, SelectiveFilterRateComparison) +{ + for (float rate : {0.0f, -1.0f}) { + for (int rep = 0; rep < 5; rep++) { + int short_queries = 0; + double recall = 0; + search_with_selective_filter(rate, short_queries, recall); + std::cout << "DIAG-CPP filtering_rate=" << rate << " rep=" << rep + << " short_queries=" << short_queries << "/500 recall=" << recall << std::endl; + } + } +} + } // namespace cuvs::neighbors::cagra diff --git a/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestDiagFilteredMultiPartitionShortfall.java b/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestDiagFilteredMultiPartitionShortfall.java index 94b2f215d2..9baa5df3b7 100644 --- a/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestDiagFilteredMultiPartitionShortfall.java +++ b/java/cuvs-lucene/src/test/java/com/nvidia/cuvs/lucene/TestDiagFilteredMultiPartitionShortfall.java @@ -61,7 +61,7 @@ private record Config(int iTopK, SearchAlgo algo) {} public void measureShortfallRate() throws Exception { int trials = Integer.getInteger("diag.trials", 6); int numQueries = Integer.getInteger("diag.queries", 64); - int[] numCategoriesOptions = {24, 48}; + int[] numCategoriesOptions = {24}; final int datasetSize = 2000; final int dimensions = 128; final int topK = 10;