Skip to content

Sync with upstream + add in-line filtered late-interaction search - #2

Draft
jakemannix wants to merge 7 commits into
mainfrom
claude/sync-upstream-k3WZN
Draft

Sync with upstream + add in-line filtered late-interaction search#2
jakemannix wants to merge 7 commits into
mainfrom
claude/sync-upstream-k3WZN

Conversation

@jakemannix

@jakemannix jakemannix commented May 17, 2026

Copy link
Copy Markdown
Owner

Summary

Two changes on this branch:

  1. Fast-forward sync of the fork with stanford-futuredata/ColBERT main (6 upstream commits, no divergence).
  2. In-line filtered late-interaction search — a new extension that walks filter inverted lists in parallel with IVF cluster lists during candidate generation, instead of pre- or post-filtering.

In-line filtering design

The current ColBERT path is: walk cluster lists → concat → sort → unique → apply filter_fn → score. The new path runs the filter during the cluster-list traversal, so the sort/unique only sees the already-filtered set. For selective filters this is a big reduction in dedup work, candidate-pool memory, and downstream centroid-pruning work.

New modules:

  • colbert/search/filter_index.py
    • FilterIndex.from_field_data(...) — builds inverted lists keyed by (field, value), packed in a StridedTensor of int32 PIDs.
    • FilterExpression (Term / And / Or) — compose filters; .materialize(filter_index, mode='auto'|'bitmap'|'sorted') compiles to a runtime filter.
    • MaterializedFilter: BitmapFilter (bool tensor of length num_passages, branch-free / GPU-friendly) and SortedListFilter (sorted-ascending PIDs + searchsorted).
    • Both expose filter_packed(packed_pids, lengths) → (filtered_packed, filtered_lengths) — the primitive invoked during cell-list traversal.
  • colbert/search/filtered_search.py
    • filtered_ivf_lookup(ivf, cell_ids, materialized_filter) — vectorised single-pass implementation.
    • filtered_ivf_lookup_per_cell(...) — reference per-cell merge-join (canonical algorithm; mirror layout for a future per-block CUDA kernel).

Wiring (additive, backward-compatible):

  • CandidateGeneration.generate_candidates accepts materialized_filter=... and dispatches to the new code path when provided.
  • IndexScorer.retrieve / IndexScorer.rank pass it through. The legacy filter_fn callback still works and stacks on top.
  • Searcher.search / search_all / dense_search plumbed end-to-end.

GPU acceleration plan:

  • Hybrid (current ColBERT, data in RAM): bitmap path = one indexed gather + one scatter_add_ (CUDA-friendly torch ops); sorted-list path = searchsorted + segment-sum.
  • Full-VRAM (small index): same filter_packed(packed_pids, lengths) signature lowers directly to a fused CUDA kernel reading IVF + bitmap in one pass. The _per_cell_reference form intentionally maps to one CUDA block per cell with warp-cooperative merge inside.

Test plan

  • colbert/tests/inline_filter_test.py — 27 unit tests, CPU-only, no model/index needed:
    • FilterIndex construction (single, multi-valued, missing values, unknown terms)
    • Expression materialisation (Term, And, Or, nested)
    • Auto-mode density heuristic
    • Both materialised filter primitives (BitmapFilter / SortedListFilter) against a brute-force reference
    • filtered_ivf_lookup matches filtered_ivf_lookup_per_cell matches brute-force reference
    • Randomised correctness: 6 seeds, random IVF + random filter + random cell selection — in-line filtered candidate gen ≡ legacy (union → dedup → filter) candidate set
    • End-to-end through CandidateGeneration.generate_candidates with the materialized_filter kwarg
  • Benchmark on a real ColBERT index (deferred; needs GPU + actual residual encoder)
  • CUDA kernel implementation of filter_packed for the full-VRAM mode (deferred)

Commits

  • cc4f3dc (and 5 prior) — upstream sync
  • 546f088 — in-line filtered search

vishalbakshi and others added 7 commits July 27, 2025 18:07
…pdate_version

Update version to 0.2.22 in setup.py
…/bugfix/multigpu-data

bugfix : Incorrect sample division for "Single-Node, Multi-GPU" ddp training

Handles sample division in colbert/data/examples.py with:

```python
[self.data[idx + rank] for idx in range(0, len(self.data), nranks) if idx + rank < len(self.data)]
```
Introduces an inverted-list-based filter that is traversed in parallel
with IVF cluster lists during candidate generation, replacing the
existing post-filter pattern with a true in-line one. The downstream
sort/unique step then operates on the already-filtered packed PIDs
rather than on the unfiltered union, which is the main savings.

New modules:
  - colbert/search/filter_index.py
      FilterIndex (term -> sorted PID list, packed in a StridedTensor),
      FilterExpression (Term / And / Or),
      MaterializedFilter (BitmapFilter / SortedListFilter), each exposing
      filter_packed(packed_pids, lengths) and a per-cell merge-join
      reference path.
  - colbert/search/filtered_search.py
      filtered_ivf_lookup (vectorised) and filtered_ivf_lookup_per_cell
      (reference per-block layout for a future CUDA kernel).

Hooks (additive, backward-compatible):
  CandidateGeneration.generate_candidates, IndexScorer.retrieve/rank,
  and Searcher.search/search_all/dense_search/_search_all_Q all accept
  a new optional materialized_filter kwarg. The existing filter_fn
  callback path is unchanged.

GPU plan: the filter_packed primitive is the natural lowering target
for a fused CUDA kernel that reads from the IVF tensor and the filter
bitmap (or sorted list) in one pass; for the full-VRAM mode the
BitmapFilter path is already branch-free and coalesced.

Tests: colbert/tests/inline_filter_test.py exercises FilterIndex
construction, expression materialisation, both filter representations,
the vectorised and per-cell lookup paths, and randomised equivalence
between in-line filtered candidate gen and the legacy post-filter
path. 27 tests, all passing on CPU.

https://claude.ai/code/session_01M8ykB5AJXoBNknM8dsH8tm
@jakemannix jakemannix changed the title Sync with upstream stanford-futuredata/ColBERT main Sync with upstream + add in-line filtered late-interaction search May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants