Sync with upstream + add in-line filtered late-interaction search - #2
Draft
jakemannix wants to merge 7 commits into
Draft
Sync with upstream + add in-line filtered late-interaction search#2jakemannix wants to merge 7 commits into
jakemannix wants to merge 7 commits into
Conversation
…dd_roadmap Add ROADMAP.md
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two changes on this branch:
stanford-futuredata/ColBERTmain(6 upstream commits, no divergence).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.pyFilterIndex.from_field_data(...)— builds inverted lists keyed by(field, value), packed in aStridedTensorof 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 lengthnum_passages, branch-free / GPU-friendly) andSortedListFilter(sorted-ascending PIDs +searchsorted).filter_packed(packed_pids, lengths) → (filtered_packed, filtered_lengths)— the primitive invoked during cell-list traversal.colbert/search/filtered_search.pyfiltered_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_candidatesacceptsmaterialized_filter=...and dispatches to the new code path when provided.IndexScorer.retrieve/IndexScorer.rankpass it through. The legacyfilter_fncallback still works and stacks on top.Searcher.search/search_all/dense_searchplumbed end-to-end.GPU acceleration plan:
scatter_add_(CUDA-friendly torch ops); sorted-list path =searchsorted+ segment-sum.filter_packed(packed_pids, lengths)signature lowers directly to a fused CUDA kernel reading IVF + bitmap in one pass. The_per_cell_referenceform 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:FilterIndexconstruction (single, multi-valued, missing values, unknown terms)Term,And,Or, nested)BitmapFilter/SortedListFilter) against a brute-force referencefiltered_ivf_lookupmatchesfiltered_ivf_lookup_per_cellmatches brute-force reference(union → dedup → filter)candidate setCandidateGeneration.generate_candidateswith thematerialized_filterkwargfilter_packedfor the full-VRAM mode (deferred)Commits
cc4f3dc(and 5 prior) — upstream sync546f088— in-line filtered search