IVF-Flat
UDTF: cuvs_ivf_flat
Official cuVS reference: C API
Query-local inverted-file approximate nearest-neighbor search.
Quickstart
The call below expects the registered relations dataset_vectors (the dataset role) and query_vectors (the queries role), each passed as a parenthesized SELECT subquery. Substitute your own relations and column names.
SELECT *
FROM cuvs_ivf_flat(
dataset => (SELECT id, d0, d1 FROM dataset_vectors),
queries => (SELECT id, d0, d1 FROM query_vectors),
k => 8,
metric => 'l2_expanded',
n_lists => 16,
n_probes => 16
)
ORDER BY query_ordinal, rank;
Inputs
Each relation argument is a parenthesized SELECT subquery that the planner keeps as a real child; metadata validation resolves a registered table or view for the same role instead. See Vector Inputs for the relation identity rules and the ID, dense-vector type, null, finite-value, and runtime-dimension contract.
Vector element types
Arguments and options
Scalar SQL arguments
SQL value argument schemas
Vector binding shapes
Each relation subquery must project a non-null id field followed by either one or more non-null feature fields of a supported element type (Float32, Int8, UInt8) or one non-null list vector field named vector.
For wide vectors, the projection order defines the feature dimensions. A list vector relation must contain no feature field beside id and vector.
Output
Concrete schemas are call-specific. Run gpu_validate_call against registered relations to inspect the output schema after the actual ID types and literal options are validated.
Examples
This example uses the EMBER2024 demo dataset.
Which earlier Win32 files make useful references for a new quarter?
An analyst reviewing a new Win32 file can retrieve structurally similar files from an earlier period and inspect their historical labels and families. The search corpus contains 15,588 sampled train files from weeks 0-51; the held-out test period contains 3,618 sampled files from weeks 52-63. Both use the same one-percent SHA-256 sample and 512 normalized byte and byte-entropy histogram features. The train and test SHA-256 sets are disjoint. The search UDTFs receive only IDs and histogram vectors. Labels, family names, week IDs, and SHA-256 values are joined after search for review. The query retrieves references; it does not classify malware.
The family rates measure agreement with the available family labels. For the 1,575 test queries with a known family, 35.39% of brute-force top-10 reference slots had the same known family; references with no family label count as nonmatches. A uniform random training reference gives a 2.76% expected same-family rate. The search input does not use those labels or families.
For test file 10021055 from week 52, the exact result's three nearest
references are from weeks 9, 16, then 10. Their family labels appear only after
the search join:
At this cohort size brute force had the lowest median whole-statement time. Each time includes per-statement index construction and fetching all 36,180 result rows. The captures ran on the dedicated EMBER server. They are not a controlled performance study, so the timings do not support an ANN speedup claim.
The exhaustive brute-force search uses Float32 squared-L2 arithmetic. Its
0.9990 ID recall against direct Float64 distances reflects changes near the
top-10 boundary; the measured maximum distance error was 6.72e-6.
Download the capture JSON, including all three exact SQL statements, run query IDs, CPU-oracle metrics, post-search family measurements, and conditions.
- Exact brute force
- CAGRA
- IVF-Flat
WITH train_index AS (
SELECT v.id, v.histogram AS vector
FROM ember_research_vectors v
JOIN ember_metadata m ON m.id = v.id
WHERE v.split = 'train' AND m.file_type = 'Win32'
ORDER BY v.id
), test_queries AS (
SELECT v.id, v.histogram AS vector
FROM ember_research_vectors v
JOIN ember_metadata m ON m.id = v.id
WHERE v.split = 'test' AND m.file_type = 'Win32'
ORDER BY v.id
)
SELECT n.query_ordinal,
n.query_id,
qm.week_id AS query_week,
q.label AS query_label,
n.rank,
n.neighbor_id,
tm.week_id AS neighbor_week,
train.label AS neighbor_label,
tm.sha256 AS neighbor_sha256,
tm.family AS neighbor_family,
n.distance
FROM cuvs_brute_force_knn(
dataset => (SELECT id, vector FROM train_index ORDER BY id),
queries => (SELECT id, vector FROM test_queries ORDER BY id),
k => 10,
metric => 'l2_expanded'
) n
JOIN ember_research_vectors q ON q.id = n.query_id AND q.split = 'test'
JOIN ember_metadata qm ON qm.id = q.id
JOIN ember_research_vectors train ON train.id = n.neighbor_id AND train.split = 'train'
JOIN ember_metadata tm ON tm.id = train.id
ORDER BY n.query_ordinal, n.rank;
Dedicated r3 capture query ID: 73f3ec94-7127-4b7f-95a7-6a949ceeaa98.
-- CAGRA approximate search over the same ordered Win32 train and test cohorts
-- used by 20_cuvs_brute_force_knn.sql.
WITH train_index AS (
SELECT v.id, v.histogram AS vector
FROM ember_research_vectors v
JOIN ember_metadata m ON m.id = v.id
WHERE v.split = 'train' AND m.file_type = 'Win32'
ORDER BY v.id
), test_queries AS (
SELECT v.id, v.histogram AS vector
FROM ember_research_vectors v
JOIN ember_metadata m ON m.id = v.id
WHERE v.split = 'test' AND m.file_type = 'Win32'
ORDER BY v.id
)
SELECT n.query_ordinal,
n.query_id,
qm.week_id AS query_week,
q.label AS query_label,
n.rank,
n.neighbor_id,
tm.week_id AS neighbor_week,
train.label AS neighbor_label,
tm.sha256 AS neighbor_sha256,
tm.family AS neighbor_family,
n.distance
FROM cuvs_cagra(
dataset => (SELECT id, vector FROM train_index ORDER BY id),
queries => (SELECT id, vector FROM test_queries ORDER BY id),
k => 10,
metric => 'l2_expanded',
graph_degree => 32,
intermediate_graph_degree => 64
) n
JOIN ember_research_vectors q ON q.id = n.query_id AND q.split = 'test'
JOIN ember_metadata qm ON qm.id = q.id
JOIN ember_research_vectors train ON train.id = n.neighbor_id AND train.split = 'train'
JOIN ember_metadata tm ON tm.id = train.id
ORDER BY n.query_ordinal, n.rank;
Dedicated r3 capture query ID: c0f13481-511d-4600-815e-b43ab2685418.
-- IVF-Flat approximate search with 128 lists and 16 probes. Keep the input
-- cohorts aligned with the exact-search baseline for later recall comparison.
WITH train_index AS (
SELECT v.id, v.histogram AS vector
FROM ember_research_vectors v
JOIN ember_metadata m ON m.id = v.id
WHERE v.split = 'train' AND m.file_type = 'Win32'
ORDER BY v.id
), test_queries AS (
SELECT v.id, v.histogram AS vector
FROM ember_research_vectors v
JOIN ember_metadata m ON m.id = v.id
WHERE v.split = 'test' AND m.file_type = 'Win32'
ORDER BY v.id
)
SELECT n.query_ordinal,
n.query_id,
qm.week_id AS query_week,
q.label AS query_label,
n.rank,
n.neighbor_id,
tm.week_id AS neighbor_week,
train.label AS neighbor_label,
tm.sha256 AS neighbor_sha256,
tm.family AS neighbor_family,
n.distance
FROM cuvs_ivf_flat(
dataset => (SELECT id, vector FROM train_index ORDER BY id),
queries => (SELECT id, vector FROM test_queries ORDER BY id),
k => 10,
metric => 'l2_expanded',
n_lists => 128,
n_probes => 16
) n
JOIN ember_research_vectors q ON q.id = n.query_id AND q.split = 'test'
JOIN ember_metadata qm ON qm.id = q.id
JOIN ember_research_vectors train ON train.id = n.neighbor_id AND train.split = 'train'
JOIN ember_metadata tm ON tm.id = train.id
ORDER BY n.query_ordinal, n.rank;
Dedicated r3 capture query ID: 02f1af3f-270f-4a22-8559-dee13bd59478.
Limits
- Validation resolves named tables or views and reads schemas only; it does not execute relation scans or GPU work.
- Execution relation arguments require parenthesized subqueries; dry-run validation accepts registered named relations only.
- Builds and destroys its query-local index in the statement; no index persists across statements.
- Results are approximate unless all lists are probed. Fewer than k neighbors may be returned for a query; missing (query, rank) rows are omitted and valid ranks remain contiguous.
- The evaluated dataset must be non-empty, k and n_lists must not exceed its row count, and n_probes must not exceed n_lists.
- Dataset and query dimensions and element types must match. Cosine requires at least two dimensions.
To dry-run validate relation metadata, column types, and options without execution, see gpu_validate_call.