Contents:
- 1. Vector Format
- 2. Scalar Functions
- 3. Training
- 4. Virtual Table
- 4.1. Creation
- 4.2. Configuration
- 4.3. Nearest-Neighbor Queries
This page is intended to be a complete reference to the interface exported by the vec1 extension. It is not a user manual. For that, please see this page.
1. Vector Format
Vec1's native vector format is an SQL BLOB containing a packed array of 32-bit IEEE floating point values in machine byte order. All values must be finite, NaN and infinite values are not allowed.
2. Scalar Functions
Vec1 provides the following SQL scalar functions:
vec1_info()
This function returns a human-readable string describing the version of the vec1 extension currently in use.
vec1_l2_distance(VECTOR1, VECTOR2)
Both arguments must be vectors in native format. Both must be the same size. This function returns the square of the euclidean distance between the two vectors (SQL type real), calculated as:
vec1_l2_distance(a, b) = ∑i(ai - bi)2
If either argument is not an SQL BLOB, or if the two arguments are of different sizes, or if the size in bytes of the two arguments is not divisible by 4, an exception is thrown.
vec1_cos_distance(VECTOR1, VECTOR2)
Like vec1_l2_distance(), except that it returns the cosine distance between
the two vectors (SQL type real), calculated as follows:
vec1_cos_distance(a, b) = 2.0 - ∑i(ai bi) / √((∑i ai²)(∑i bi²))
This function does not require its arguments to be normalized.
vec1_to_json(VECTOR)
The argument to this function must be a vector in native format. A JSON array containing the elements of the vector is returned (SQL type TEXT).
vec1_from_json(JSON)
The argument to this function must be a JSON array of numeric values. It returns the equivalent vector in native format (a BLOB).
vec1_config(PARAMETER) / vec1_config(PARAMETER, VALUE)
The first form returns the current value of parameter PARAMETER (type TEXT). The second form sets the value of PARAMETER to VALUE, then returns a copy of the new value. Supported parameters are currently:
nthread. Integer. The number of threads to use for various operations, including the main thread. The default value is 1.nprobe. Real. The defaultnprobevalue to use in for queries against vec1 tables. Default value 0.05.
Example
To configure vec1 to use 16 threads when possible:
SELECT vec1_config('nthread', 16);
To query for the current default value of nprobe:
SELECT vec1_config('nprobe');
3. Training
The IVF+OPQ algorithm used by vec1 requires a trained model. This model is
built by training on a representative set of vectors (i.e. vectors with the
same data distribution as those that will be indexed).
There is a single aggregate SQL function used for training:
vec1_train(VECTOR, JSON-PARAMETERS)
The aggregate should be run over the set of training vectors, each vector passed as the first argument. It returns a BLOB containing the trained model, which may be used with a vec1 virtual table. The second argument is a JSON object (type TEXT) containing parameters to configure the model and training process. The following parameters are supported to configure the model:
distance. The distance metric to use. Must be either "l2" or "cos". The default is "l2".nbucket. Integer. The number of buckets used by the inverted file (IVF). If set to 0, IVF is disabled and queries perform an exhaustive search over all (possibly compressed) vectors.quantizer. A string identifying the quantizer to use to compress vectors within each bucket. There are four possible values:- "none" - do not compress vectors. Instead, store full vectors in each bucket.
- "pq" - Product Quantization.
- "opq" - Optimized Product Quantization.
- "bq" - Binary Quantization.
codesize. Integer. The size in bytes of the quantized vectors. This is advisory only - because not all sizes can be implemented by all quantizers, in practice vec1 picks the nearest possible size to the specified value.The default value depends on the value of the "quantizer" field. If quantizer is set to "none", this value defaults to 0. If it is set to "pq" or "bq", the default of this value is 1 byte for each 8 vector dimensions (rounded up). Or, if quantizer is set to "opq", this value defaults to 32.
residual. Boolean. If true and bothnbucketandcodesizeare greater than 0, then residual instead of full vectors are compressed and stored in the index. The default is true.wht. Boolean. If true, a randomized Walsh-Hadamard transformation is applied to all data and query vectors before processing. The default is true if quantizer is set to "bq", or false otherwise.
And the following parameters are also supported to configure the training process itself:
progress. The name of an SQL function registered with the database connection. During training, it is invoked periodically as if by:SELECT progress (:percent, :msg);The first argument (:percent) is an integer indicating the approximate percentage of work completed. The second (:msg) is a human-readable message describing the current stage in English.profile. The name of an SQL function registered with the database connection. It is invoked during training as if by:SELECT profile (:json);The only argument (:json) is a json object containing a series of attributes that allow performance profiling of the training algorithm. The attributes of the object and the number of time the profile callback is invoked during training are not strictly defined and may change between versions. They are intended to be reasonably intuitive though.nthread. Integer. The number of CPU threads to use for training, including the main thread. If not specified, the default configured viavec1_config()is used. If no default is set, training is single-threaded.nopq_round. Integer. The number of iterations of the OPQ algorithm to use to calculate a rotation, if it is enabled. The default value is 5.svd_verify. Boolean. If true, additional checks are performed on each Singular Value Decomposition (SVD) computed during OPQ training. This is computationally expensive and should only be enabled when debugging. Default: false.opq. Boolean. Now deprecated in favor ofquantizer:"opq".
Models may be generated on one machine and used on another. However, the two machines must use the same byte-order for 32-bit IEEE floating point values.
Example
Assume table "learn" contains one training vector per row in column "vec". The following query generates a model:
-- Returns a model (an SQL BLOB) for use with a vec1 virtual table.
--
-- Configuration:
-- distance = "cos" -> use cosine distance
-- quantizer = "opq" -> use OPQ to compress vectors
-- codesize = 32 -> compress each vector to 32 bytes
-- nbucket = 1024 -> use 1024 IVF buckets
--
SELECT vec1_train(learn.vec, '{
distance: "cos",
quantizer: "opq",
codesize: 32,
nbucket: 1024
}')
FROM learn;
4. Virtual Table
4.1. Creation and Population of Tables
To create a vec1 virtual table:
CREATE VIRTUAL TABLE tbl USING vec1(vector_column, metadata_column...);
The first argument specifies the name of the column used to store vectors. Each subsequent argument specifies the name of a metadata column.
A vec1 table must contain exactly one vector column, and may contain
between 0 and 255 metadata columns. All vec1 tables have a unique integer
rowid that identifies each row. Additionally, all vec tables have a
hidden column named distance populated dynamically by nearest-neighbor
queries.
Column names may be enclosed in single or double quotes. If unquoted, they must consist only of ASCII alphanumeric characters and underscores. The names "rowid" and "distance" are reserved and may not be used.
It is not possible to specify types or other column constraints for
vec1 table columns.
vec1 virtual tables may be modified using standard SQL INSERT, UPDATE,
and DELETE statements. The vector column accepts only BLOB values whose
length is a multiple of 4 bytes (each element is a 32-bit floating-point
value). Once the vector size for a table has been fixed, all inserted vectors
must have exactly that size.
The vector size is fixed when either:
- The first vector is inserted, or
- A model generated by
vec1_train()is applied to the table.
Metadata columns may store values of any type.
4.2. Configuration
A vec1 table may be configured to use a model using the following SQL
command:
INSERT INTO tbl(cmd, arg) VALUES('rebuild', MODEL);
MODEL must either be a BLOB returned by vec1_train(), or else a
JSON object (type TEXT) specifying model parameters. Supported model
parameters for JSON models are:
distance. Text. Must be set to either "l2" or "cos" to specify the distance metric to be used by the table.index. Text. Must be set to either "none" or "flat". A "none" index stores each vector in its own row of an SQLite table. The only reason to explicitly configure a "none" index is to set the distance metric. A "flat" index stores all vectors in large packed SQL BLOBs. This can provide a 2x performance improvement over storing each vector in its own row.
The 'rebuild' command automatically rebuilds the index using the new model. Subsequent 'rebuild' commands replace the existing index. 'rebuild' may be run before or after vectors are added to the table.
Example
To configure a vec1 table to store full vectors in packed BLOBs and to use cosine distance when queried:
INSERT INTO tbl(cmd, arg) VALUES('rebuild', '{
index: "flat",
distance: "cos"
}');
4.3. Nearest-Neighbor Queries
A vec1 table is queried for the nearest-neigbours of a vector by using
the table as a table-valued-function, where the first argument is the query
vector, and the second, optional, argument the query parameters. As follows:
SELECT ... FROM tbl(VECTOR, PARAMETERS)
If PARAMETERS is passed a TEXT value, it is interpreted as a JSON object containing query parameters. The following query parameters are supported:
K. Integer. The number of results required.nprobe. A real value. This value must be greater than 0.0. If a value less than 1.0 is specified, then it is the fraction of IVF buckets that should be searched for nearest-neighbors. If the value is 1.0 or greater, it is truncated to an integer and used as the number of buckets to scan searching for nearest-neighbors. If this parameter is not specified, the connection-wide default configured byvec1_config()is used. If no default has been configured, 0.05 is used.streaming. Boolean. If this parameter is false (the default), thenKandnprobeare both hard limits - the virtual table will never return more thatKresults or scan more thannprobebuckets. Ifstreamingis set to true, then these parameters are both advisory, and the virtual table continues to return results until the SQL engine stops requesting them.
The hidden distance column is populated with the distance between the query
vector and the row vector for each row returned. If the table is configured
with a model that uses compressed vectors (i.e. was trained with a non-zero
codesize parameter), then the distances returned in this column are based
on the compressed version of the vector. For non-streaming queries, rows are
always returned in ascending order of this column.
For streaming queries, rows are usually returned in ascending order of
distance, but some results may also be returned slightly out of order.
This happens when the SQL engine requests so many rows that the query has
to begin scanning more than the number of buckets suggested by the nprobe
parameter.
If PARAMETERS is passed an INTEGER value instead of TEXT, it is equivalent to
specifying the integer as the K value and leaving all other query parameters
unset.
WHERE clause processing
If a nearest-neighbor query has a WHERE clause that specifies one or more
constraints connected by AND operators that meet the following criteria, then
they are evaluated internally by vec1. This changes the query results because
this filtering occurs before the best K results are accumulated. The
criteria are:
- The constraint must use either "IS NULL" or "IS NOT NULL", or else
one of the binary operators
>,<,=,>=,<=orIS, and - One side of the operator must be a metadata column of the vec1 table, and
- the other side is either a constant expression or else an expression that may be evaluated using values read only from FROM clause elements that SQLite scans before the vec1 table within the query.
LIMIT clause processing
Normally, a nearest-neighbor query requires a K value to be specified.
However, if a query against a vec1 table as a visible LIMIT clause, then
the value passed to the LIMIT clause is used in place of an explicit K.
Or, if a query has both an explicit K and a visible LIMIT clause, then
the smaller of the two values is selected at runtime. Whether or not an
SQL LIMIT clause is visible is determined by the SQL engine. In general,
a LIMIT clause is visible if the query (or sub-query) that uses the vec1
table:
- is not a join or an aggregate,
- has no ORDER BY clause,
- has no WHERE clause, or else a WHERE clause that consists entirely of AND connected constraints on metadata columns that vec1 can handle internally.