Tasks/Systems & Hardware

10M-Scale Metadata-Filtered ANN Search

Optimize single-core top-10 vector retrieval under one- and two-tag predicates

Systems & Hardwarevector searchmetadata filtering
Background

Filtered vector search, nearest-neighbour retrieval from an embedding index restricted to items matching a metadata predicate, still lacks a strategy that stays fast whether a predicate admits a few items or millions. The starting point is a textbook system over ten million tagged embeddings: an inverted index resolves the tags, one global clustering index prunes, and survivors are scored exactly. The work is to redesign candidate selection and ranking for speed. Single-core timing and a strict recall floor rule out brute force and parallelism.

instruction.mdthis is what the agent is given

A photo library holds 10,000,000 images. Each is a 192-dimensional uint8 embedding plus a set of metadata tags (uploader, camera, place, year, and so on). A query arrives as an embedding plus a tag predicate: one or two tags. An image is a valid answer only if its tag set contains every tag in the predicate. Among the valid images, return the 10 nearest by squared L2 distance. Your job is to make that fast.

Hard Constraints

  • Edit /app/methods/main/solver.py in place. /app/methods/main/ is what gets graded.
  • Keep the exact Solver class contract below. build() is called once, then search_batch() is called once with the entire workload; the row order of what search_batch() returns must match the query order.
  • Answers must satisfy the predicate: an image counts only if its tag set contains every tag in the query's predicate.
  • build() may use every core. Before the timed call every thread of the process is pinned to one CPU (sched_setaffinity, applied to each existing thread and inherited by any created later), so search throughput cannot be bought with parallelism.
  • There is no network at run time: whatever you use has to be already installed or written here. pip list and the image's package manifest are the authoritative record of what is available.

What You Have

/app/data/ holds the library in its published binary form:

file contents
base.10M.u8bin int32 n, int32 d, then n*d raw bytes -- the [10000000, 192] uint8 matrix
base.metadata.10M.spmat int64 nrow, ncol, nnz, then int64 indptr[nrow+1], then int32 indices[nnz] -- the tag sets as CSR
query_vectors.npy 500 visible queries, uint8 [500, 192]
query_tag_indptr.npy, query_tag_indices.npy the visible queries' predicates, same CSR convention
ground_truth.npy the correct top-10 row ids for each visible query, int32 [500, 10]

ann_utils.py (next to your solver) has readers for both binary formats, the tag predicate, a squared-L2 helper, an inverted-index builder and a plain k-means IVF index. All of it is yours to use, rewrite or delete.

base_vectors is memory-mapped. The container has 32 GB of RAM.

Run python /app/selfcheck.py to get your own recall@10 and QPS on the visible queries under the same protocol as the grader, including the one-CPU pinning. It reports the raw metrics only.

What You Submit

Leave your best implementation in /app/methods/main/solver.py, exposing exactly:

class Solver:
    def build(self, base_vectors, tag_indptr, tag_indices):
        """Called once, before search_batch(). Untimed, and free to use every core.
        base_vectors : uint8 memmap [10000000, 192]
        tag_indptr   : int64 [10000001]   CSR row pointers
        tag_indices  : int32 [108210476]  row i carries tags tag_indices[indptr[i]:indptr[i+1]]
        """

    def search_batch(self, query_vectors, query_tag_indptr, query_tag_indices, k=10):
        """Called ONCE with the entire workload. This call is what gets timed.
        query_vectors    : uint8 [n, 192]
        query_tag_indptr : int64 [n + 1]    same CSR convention as the library's tags
        query_tag_indices: int32 [nnz]      query i has tags indices[indptr[i]:indptr[i+1]]
        returns          : [n, k] library row indices; row order must match the queries,
                           order within a row does not matter
        """

There is no submit step and no per-attempt feedback. Work and self-check for as long as your run window allows, then leave your best solver.py in place.

How It Is Judged

Two numbers, both computed on a sealed query workload you never see — a different, larger sample from the same source as your visible queries:

  • recall@10 against the official ground truth, averaged over the hidden queries. This is a gate: below 0.90 the submission scores zero no matter how fast it is.
  • QPS = hidden query count divided by the wall-clock seconds of the single search_batch() call. build() is not counted and has its own, generous, budget.

Your score rises monotonically with QPS once the gate is met, and is not capped at the top. The starter as shipped is the zero of that scale: submitted unchanged it scores 0.

Rollouts

88 minWall clock
$29.29Spend
44.1MTokens
33Versions, 28 kept

On the visible set

0 1,500 3,000 4,500 6,000 7,500 0 10 20 30 40 Agent step QPS @ recall 0.90 ↑ v0 v1 v2 v19 v20 v21 v22 v23 v25 v30 v32 v33
keptrolled backsubmitted
  1. v0The agent started from the inherited inverted-index and IVF baselineofficial recall@10 0.9278; QPS 101.332 min · $0.36
  2. v1The agent searched from the rarer tag and scored small predicates exactlyofficial recall@10 0.9850; QPS 116.116 min · $0.98
  3. v2The agent grouped frequent-tag postings by IVF cellofficial recall@10 0.9842; QPS 175.3911 min · $1.73
  4. v3The agent fused AVX-512 distance scoring and top-kcached recall@10 0.9854; warm QPS 557.0315 min · $2.42
  5. v4The agent replaced centroid differences with norm-minus-dot selectioncached recall@10 0.9376; warm QPS 1,228.216 min · $2.69
  6. v6The agent fixed native intersections and widened exact pair scoringcached recall@10 0.9860; warm QPS 1,188.419 min · $3.24
  7. v7The agent moved centroid selection to a single-thread native kernelcached recall@10 0.9466; warm QPS 1,694.228 min · $4.90
  8. v8The agent raised the binary-to-merge crossover for skewed intersectionspair scan 136.8–146.0 ms; 612,829 candidates unchanged29 min · $5.26
  9. v9The agent added dense membership bitmaps for frequent tagscached recall@10 0.9466; warm QPS 2,834–2,85431 min · $5.65
  10. v10The agent adapted probe counts to candidate-set sizecached recall@10 0.9338; warm QPS 3,50533 min · $6.39
  11. v11The agent pre-widened queries and vectorized bitmap filteringcached recall@10 0.9338; warm QPS 3,627.735 min · $6.82
  12. v12The agent cached the worst top-k slot between insertionsrecall@10 unchanged at 0.9338; warm QPS 3,632–3,74135 min · $7.03
  13. v13The agent prefetched vector cache lines eight candidates aheadcached recall@10 0.9338; warm QPS 5,455.2336 min · $7.26
  14. v14The agent doubled the vector prefetch lead, which regressedrecall@10 unchanged at 0.9338; warm QPS 5,217.8736 min · $7.48
  15. v15The agent halved the vector prefetch lead, which also regressedrecall@10 unchanged at 0.9338; warm QPS 5,194.9237 min · $7.76
  16. v16The agent shortlisted with int8 PCA before exact rerankingcached recall@10 0.9300; warm QPS 6,593.8443 min · $9.47
  17. v17The agent used a heap to maintain the PCA shortlistR100 recall@10 0.9300; warm QPS 5,378.9944 min · $9.93
  18. v18The agent lengthened PCA prefetch and selected a 64-row shortlistR64 recall@10 0.9302; warm QPS 5,799.0946 min · $10.40
  19. v19The agent locked IVF-2048 settings for its first full official checkofficial recall@10 0.9292; QPS 4,943.1251 min · $12.08
  20. v20The agent returned to IVF-1024 with PCA-capped scoringofficial recall@10 0.9312; QPS 5,212.8557 min · $14.29
  21. v21The agent tried coarser IVF-512, which hurt official throughputofficial recall@10 0.9290; QPS 4,777.0860 min · $15.56
  22. v22The agent tested IVF-768 as a speed-quality compromiseofficial recall@10 0.9308; QPS 5,053.5063 min · $17.84
  23. v23The agent tightened the IVF-768 probe schedule for speedofficial recall@10 0.9290; QPS 4,224.5867 min · $19.30
  24. v24The agent batched all query PCA projectionscached recall@10 0.9296; warm QPS 6,634.4368 min · $19.79
  25. v25The agent moved the full query loop into native codeofficial recall@10 0.9290; QPS 5,225.9772 min · $21.18
  26. v26The agent sorted queries by dominant tag, which did not helprecall@10 unchanged at 0.9296; warm QPS 9,332.8073 min · $21.54
  27. v27The agent increased the PCA prefetch lead to 32 rowsoutputs unchanged; warm QPS 9,212.58 vs 9,082.3274 min · $22.35
  28. v28The agent increased the PCA prefetch lead to 64 rows, which regressedoutputs unchanged; warm QPS 8,999.52 vs v27 9,339.9275 min · $22.69
  29. v29The agent cached the worst centroid slot between insertionsoutputs unchanged; warm QPS 9,595.23 vs 9,519.2777 min · $23.42
  30. v30The agent reduced the PCA shortlist to 50 rowsofficial recall@10 0.9206; QPS 6,959.4079 min · $24.54
  31. v31The agent hardened uncommon fallback and nondefault-k pathscached recall@10 0.9202; warm QPS 12,185.81; 500/500 predicates valid82 min · $26.11
  32. v32The agent concatenated selected cell rows into reusable scratch spaceofficial recall@10 0.9206; QPS 5,530.1985 min · $27.38
  33. v33The agent reserved reusable native buffers to reduce first-call overheadofficial recall@10 0.9206; QPS 5,185.2287 min · $28.77

On the hidden set

Original metricNormalised score
Starter184.54710.0
Frontier-calibrated reference3682.18560.6
Upper132714.341.0
This run (GPT-5.6-sol)4,775.210.6290
218 minWall clock
$34.11Spend
47.8MTokens
9Versions, 7 kept

On the visible set

0 3k 6k 9k 12k 15k 0 40 80 120 160 Agent step QPS @ recall 0.90 ↑ v0 v1 v2 v3 v4 v5 v6 v7 v8
keptrolled backsubmitted
  1. v0The agent inherited the starter IVF plus inverted-index numpy solver91.26 min · $0.90
  2. v1The agent rewrote the scan as one batched AVX-512 VNNI exact kernelReplace per-query numpy with one C call that scans every matching row: the recall ceiling and the speed floor.151.614 min · $2.45
  3. v2The agent added per-tag IVF with PQ fast-scan and exact rerankCut the bytes touched per candidate: score a 32-byte PQ code with vpshufb, pay the exact distance only on a rerank list.9,81873 min · $9.60
  4. v3The agent built a two-level quantizer with adaptive budget and NUMA-local pagesBudget probes by predicate size instead of a fixed nprobe, and re-touch the 6 GB index from the timed CPU.10,214167 min · $23.57
  5. v4The agent reordered queries by tag and split the two-tag budget11,498178 min · $26.07
  6. v5The agent retuned the adaptive scan budget toward a bigger floor13,177182 min · $26.99
  7. v6The agent added edge-case guards, prefetching and a post-build warm-up13,252201 min · $30.37
  8. v7The agent added per-query early termination on centroid distance13,712208 min · $31.90
  9. v8The agent tried early exit inside the exact rerank and lost throughput10,469215 min · $33.65

On the hidden set

Original metricNormalised score
Starter184.54710.0
Frontier-calibrated reference3682.18560.6
Upper132714.341.0
This run (Opus 5)3,620.040.5966
15 minWall clock
$1.19Spend
3.4MTokens
1version submitted

On the visible set

128 130 132 134 0 Agent step QPS at recall@10 ≥ 0.9 ↑ v0
submitted
  1. v0The agent measured and retained the starter IVF baseline131.638715 min · $1.19

On the hidden set

Original metricNormalised score
Starter184.54710.0
Frontier-calibrated reference3682.18560.6
Upper132714.341.0
This run (Gemini 3.7 Flash)127.870.0000
271 minWall clock
$15.01Spend
34.3MTokens
17Versions, 10 kept

On the visible set

0 400 800 1,200 1,600 2,000 0 4 8 12 16 Agent step QPS @ recall 0.90 ↑ v0 v1 v2 v3 v4 v5 v6 v6a v6b v7 v8 v9 v10 v11 v12 v13 final
keptrolled backsubmitted
  1. v0The agent inherited the IVF plus inverted-index starter baseline119.5$0.40
  2. v1The agent split queries into rare, mid and mega pathsRoute each query by how many rows its rarest tag has, instead of one path for every predicate.387$1.50
  3. v2The agent added an AVX2 gathered-L2 kernel and packed bitsets487$1.50
  4. v3The agent fused the filter and distance steps into single C kernels620$2.38
  5. v4The agent added PQ-ADC preranking, which lost to the AVX2 kernel476$3.27
  6. v5The agent stored per-tag cell-sorted posting lists and dropped PQStore each big tag's rows already sorted by IVF cell, so a probe is a slice and no filter pass is needed.1,213$3.27
  7. v6The agent lengthened k-means and batched the centroid matmul950$5.06
  8. v6aThe agent probed longer k-means at nlist 4096 and folded it in1,000$5.31
  9. v6bThe agent probed nlist 8192 and rejected it as slower900$5.57
  10. v7The agent added partial 64-dim filtering before an exact top-2000 rerankRank on a 64-dimension prefix first and spend the full distance only on the survivors.1,478$5.82
  11. v8The agent fused the cell-slice walk into one C kernel1,617$6.46
  12. v9The agent probed much longer k-means and rejected the tripled build1,955$8.58
  13. v10The agent moved the whole search loop into one C driver1,401$10.71
  14. v11The agent gave mega tags private per-tag IVF indexes667$12.96
  15. v12The agent cached bitset tables at build and hardened edge cases1,380$13.43
  16. v13The agent tried rate-guided probe sizing and gained no speed1,250$14.56
  17. finalThe agent submitted v12 after three fresh-build self-check runs1,350$14.79

On the hidden set

Original metricNormalised score
Starter184.54710.0
Frontier-calibrated reference3682.18560.6
Upper132714.341.0
This run (Kimi K3)1,344.470.3981
136 minWall clock
$16.57Spend
29.1MTokens
22Versions, 14 kept

On the visible set

1,000 2,000 3,000 4,000 5,000 6,000 0 5 10 15 20 Agent step QPS @ recall 0.90 ↑ v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v10* v11 v12 v13 v14 v15 v16 v17 v18 v19 v20
keptrolled backsubmitted
  1. v0The agent inherited the starter IVF baseline without measuring itnever timed
  2. v1The agent wrote an AVX-512 exact L2 scan in IVF-cell orderThe starter's cost was numpy, not arithmetic; compile exact L2 and store postings in IVF-cell order so a probe reads one run.768
  3. v2The agent swept probes and the exact-scan threshold down1,225
  4. v3The agent packed frequent tags into contiguous blocks with per-cell pointersCopy each big tag's rows into one contiguous block, so filtering is a sequential scan and extra probes turn cheap.1,353
  5. v4The agent moved the whole search batch into C and slowed down1,253
  6. v5The agent added bitsets for big tags and a fused two-tag scanProfiling put two-tag intersection at 217 of 370 ms; a bitset turns that merge into a filter over the rarer posting.2,510
  7. v6The agent halved nprobe now that two-tag queries were cheap3,230
  8. v7The agent tried two-nearest-cell assignment with per-tag spill lists2,578
  9. v8The agent lowered the packing and bitset thresholds to cover more tags4,251
  10. v9The agent batched the centroid GEMM across all queries without gain3,925
  11. v10The agent prefetched only bitset hits in the packed two-tag scan4,594
  12. v10*The agent ran the official self-check to confirm its best version4,548.6
  13. v11The agent replaced numpy cell ranking with a C heap and lost3,952
  14. v12The agent gave huge tags private k-means indexes and reverted4,143
  15. v13The agent pre-unpacked the query into registers and reused it4,733
  16. v14The agent dropped nprobe from twenty-four to twenty5,156
  17. v15The agent unrolled the contiguous scan two vectors wide5,338
  18. v16The agent cut nprobe to sixteen and rejected the thin recall margin5,433
  19. v17The agent doubled the IVF list count and lost sequential-scan speed4,195
  20. v18The agent grouped the query batch by predicate for cache locality5,294
  21. v19The agent lowered the bitset threshold and dropped unused CSR data5,663
  22. v20The agent prefaulted all index memory during the untimed build5,309

On the hidden set

Original metricNormalised score
Starter184.54710.0
Frontier-calibrated reference3682.18560.6
Upper132714.341.0
This run (Grok 4.6)2,078.370.4854
245 minWall clock
$3.18Spend
24.8MTokens
4Versions, 3 kept

On the visible set

350 375 400 425 450 475 0 1 2 2 3 Agent step QPS @ recall 0.90 ↑ v1 v2 v3
keptrolled backsubmitted
  1. v1The agent rebuilt search around per-tag k-means IVF with exact ranking360.8$0.23
  2. v2The agent matched the ground-truth tie-break and switched to k-means++ init471.563 min · $0.46
  3. v2.1The agent took a safety snapshot of the unchanged main solversnapshot only$1.80
  4. v3The agent hand-wrote SIMD distance kernels and a two-phase vectorized probe454.2245 min · $3.15

On the hidden set

Original metricNormalised score
Starter184.54710.0
Frontier-calibrated reference3682.18560.6
Upper132714.341.0
This run (DeepSeek V4 Pro)479.7810.1915
59 minWall clock
$6.37Spend
20.6MTokens
6Versions, 5 kept

On the visible set

0 200 400 600 800 1,000 0 2 3 4 Agent step QPS @ recall 0.90 ↑ v1 v2 v3 v4 v5
keptrolled backsubmitted
  1. v0The agent inherited the starter IVF baseline without measuring itinherited, never run$0.44
  2. v1The agent rewrote search with grouped per-tag lists and an AVX2 kernel643.615 min · $0.89
  3. v2The agent replaced the kernel with a VNNI dot-product dispatch chain94927 min · $2.00
  4. v3The agent added tag-local k-means with an exact suffix-bound stopping rule41.934 min · $2.58
  5. v4The agent reordered probes by lower bound and fell below the recall gate663.644 min · $3.61
  6. v5The agent restored centroid-order probing with an exact stop and tie-break956.749 min · $4.28

On the hidden set

Original metricNormalised score
Starter184.54710.0
Frontier-calibrated reference3682.18560.6
Upper132714.341.0
This run (Qwen3.8 Max)803.9070.2950
396 minWall clock
$31.27Spend
112.6MTokens
10Versions, 9 kept

On the visible set

0 400 800 1,200 1,600 0 2 4 6 8 Agent step QPS @ recall 0.90 ↑ v0 v1 v2 v3 v5 v6 v7 v8 v9
keptrolled backsubmitted
  1. v0The agent inherited the shipped starter with post-filtered IVF search12170 min · $3.62
  2. v1The agent rewrote search around tag-major IVF with PQ-ADC and rerank999152 min · $10.23
  3. v2The agent stopped the multi-tag walk at a hit goal instead of survivors1,400152 min · $10.23
  4. v3The agent rewrote the range computation around a prefix-sum bound array1,789203 min · $14.43
  5. v4The agent added a build cache and fixed its timing protocoltooling, not timed$14.43
  6. v5The agent sorted multi-tag probes by row id and split the budget1,788203 min · $14.43
  7. v6The agent retuned rerank and survivor constants on the recall-QPS frontier1,750262 min · $18.55
  8. v7The agent sharpened centroids with a larger, longer k-means1,723328 min · $24.28
  9. v8The agent stripped the build cache and tidied the imports1,760355 min · $27.61
  10. v9The agent cut k-means iterations to shorten the build1,540381 min · $29.78

On the hidden set

Original metricNormalised score
Starter184.54710.0
Frontier-calibrated reference3682.18560.6
Upper132714.341.0
This run (GLM 5.3)1,406.540.4071
85 minWall clock
$12.70Spend
19.1MTokens
24Versions, 10 kept

On the visible set

150 300 450 600 750 0 50 100 150 200 Agent step QPS @ recall 0.90 ↑ v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23
keptrolled backsubmitted
  1. v0The agent inherited the starter IVF baseline and set its acceptance rules110.5253 min · $0.31
  2. v1The agent halved nprobe, added exact small predicates and cached repeats132.4315 min · $1.30
  3. v2The agent built tag-by-cell postings so broad queries read selected cells only268.30224 min · $2.11
  4. v3The agent raised the exact one-tag threshold and paid too much speed176.1830 min · $2.69
  5. v4The agent lowered nprobe to ten and lost its recall margin277.81232 min · $3.14
  6. v5The agent copied the base vectors into RAM instead of using the memmap286.23635 min · $3.62
  7. v6The agent retested the lower nprobe after the RAM copy279.07738 min · $3.91
  8. v7The agent stopped sorting candidates before scoring and slowed down222.99140 min · $4.29
  9. v8The agent precomputed nearest cells with one batched matrix multiply200.43843 min · $4.71
  10. v9The agent added a compiled C top-k reranker with early abandonment315.04747 min · $5.45
  11. v10The agent kept candidates in nearest-cell order for the C reranker377.99949 min · $5.79
  12. v11The agent sorted the selected cells by centroid distance and slowed down307.32551 min · $6.13
  13. v12The agent checked the abandonment threshold every sixteen dimensions382.89554 min · $6.58
  14. v13The agent widened the abandonment check to every thirty-two dimensions415.8856 min · $6.93
  15. v14The agent widened the abandonment check to sixty-four dimensions403.63158 min · $7.28
  16. v15The agent replaced the distance arithmetic with a lookup table297.06160 min · $7.76
  17. v16The agent removed early abandonment and always computed full distances366.85363 min · $8.29
  18. v17The agent lowered nprobe to eleven with the C reranker324.79165 min · $8.77
  19. v18The agent added a compiled two-pointer intersection for sorted postings740.70568 min · $9.38
  20. v19The agent raised the exact one-tag threshold again and lost throughput677.41771 min · $9.79
  21. v20The agent lowered nprobe to ten with both C kernels806.55773 min · $10.31
  22. v21The agent combined ten probes with the larger exact threshold731.22975 min · $10.72
  23. v22The agent made probe count adaptive to one-tag predicate size797.83678 min · $11.30
  24. v23The agent cut broad one-tag queries to nine probes and slowed down746.07782 min · $12.02

On the hidden set

Original metricNormalised score
Starter184.54710.0
Frontier-calibrated reference3682.18560.6
Upper132714.341.0
This run (GPT-5.5)707.5590.2694

Leaderboard

Where each run landed on the sealed held-out set, on the same normalised-score scale as the anchors above.

0 0.3 0.6 1.0 1 GPT-5.6-sol codex · max 0.629 2 Opus 5 claude code · max 0.597 3 Grok 4.6 grok · xhigh 0.485 4 GLM 5.3 claude code · max 0.407 5 Kimi K3 kimi cli · max 0.398 6 Qwen3.8 Max qwen coder · xhigh 0.295 7 GPT-5.5 codex · xhigh 0.269 8 DeepSeek V4 Pro claude code · max 0.192 9 Gemini 3.7 Flash antigravity · high 0.000