Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Kane
85ef7841ec Renamed functions to l2_norm 2024-04-03 15:16:53 -07:00
24 changed files with 321 additions and 237 deletions

View File

@@ -777,6 +777,7 @@ Function | Description | Added
cosine_distance(halfvec, halfvec) → double precision | cosine distance | unreleased cosine_distance(halfvec, halfvec) → double precision | cosine distance | unreleased
inner_product(halfvec, halfvec) → double precision | inner product | unreleased inner_product(halfvec, halfvec) → double precision | inner product | unreleased
l2_distance(halfvec, halfvec) → double precision | Euclidean distance | unreleased l2_distance(halfvec, halfvec) → double precision | Euclidean distance | unreleased
l2_norm(halfvec) → double precision | Euclidean norm | unreleased
l1_distance(halfvec, halfvec) → double precision | taxicab distance | unreleased l1_distance(halfvec, halfvec) → double precision | taxicab distance | unreleased
quantize_binary(halfvec) → bit | quantize | unreleased quantize_binary(halfvec) → bit | quantize | unreleased
subvector(halfvec, integer, integer) → halfvec | subvector | unreleased subvector(halfvec, integer, integer) → halfvec | subvector | unreleased
@@ -818,6 +819,7 @@ Function | Description | Added
cosine_distance(sparsevec, sparsevec) → double precision | cosine distance | unreleased cosine_distance(sparsevec, sparsevec) → double precision | cosine distance | unreleased
inner_product(sparsevec, sparsevec) → double precision | inner product | unreleased inner_product(sparsevec, sparsevec) → double precision | inner product | unreleased
l2_distance(sparsevec, sparsevec) → double precision | Euclidean distance | unreleased l2_distance(sparsevec, sparsevec) → double precision | Euclidean distance | unreleased
l2_norm(sparsevec) → double precision | Euclidean norm | unreleased
l1_distance(sparsevec, sparsevec) → double precision | taxicab distance | unreleased l1_distance(sparsevec, sparsevec) → double precision | taxicab distance | unreleased
## Installation Notes - Linux and Mac ## Installation Notes - Linux and Mac

View File

@@ -71,8 +71,8 @@ CREATE FUNCTION cosine_distance(halfvec, halfvec) RETURNS float8
CREATE FUNCTION l1_distance(halfvec, halfvec) RETURNS float8 CREATE FUNCTION l1_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME', 'halfvec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'halfvec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_norm(halfvec) RETURNS float8 CREATE FUNCTION l2_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'halfvec_l2_norm' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION quantize_binary(halfvec) RETURNS bit CREATE FUNCTION quantize_binary(halfvec) RETURNS bit
AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -151,7 +151,7 @@ CREATE OPERATOR CLASS halfvec_cosine_ops
FOR TYPE halfvec USING hnsw AS FOR TYPE halfvec USING hnsw AS
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops, OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec), FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
FUNCTION 2 halfvec_norm(halfvec); FUNCTION 2 l2_norm(halfvec);
CREATE FUNCTION halfvec_to_vector(halfvec, integer, boolean) RETURNS vector CREATE FUNCTION halfvec_to_vector(halfvec, integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -173,6 +173,9 @@ CREATE FUNCTION sparsevec_in(cstring, oid, integer) RETURNS sparsevec
CREATE FUNCTION sparsevec_out(sparsevec) RETURNS cstring CREATE FUNCTION sparsevec_out(sparsevec) RETURNS cstring
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_typmod_in(cstring[]) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_recv(internal, oid, integer) RETURNS sparsevec CREATE FUNCTION sparsevec_recv(internal, oid, integer) RETURNS sparsevec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -182,6 +185,7 @@ CREATE FUNCTION sparsevec_send(sparsevec) RETURNS bytea
CREATE TYPE sparsevec ( CREATE TYPE sparsevec (
INPUT = sparsevec_in, INPUT = sparsevec_in,
OUTPUT = sparsevec_out, OUTPUT = sparsevec_out,
TYPMOD_IN = sparsevec_typmod_in,
RECEIVE = sparsevec_recv, RECEIVE = sparsevec_recv,
SEND = sparsevec_send, SEND = sparsevec_send,
STORAGE = external STORAGE = external
@@ -196,8 +200,8 @@ CREATE FUNCTION inner_product(sparsevec, sparsevec) RETURNS float8
CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8 CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'sparsevec_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_norm(sparsevec) RETURNS float8 CREATE FUNCTION l2_norm(sparsevec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'sparsevec_l2_norm' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_l2_squared_distance(sparsevec, sparsevec) RETURNS float8 CREATE FUNCTION sparsevec_l2_squared_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -252,4 +256,4 @@ CREATE OPERATOR CLASS sparsevec_cosine_ops
FOR TYPE sparsevec USING hnsw AS FOR TYPE sparsevec USING hnsw AS
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops, OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops,
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec), FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
FUNCTION 2 sparsevec_norm(sparsevec); FUNCTION 2 l2_norm(sparsevec);

View File

@@ -364,8 +364,8 @@ CREATE FUNCTION cosine_distance(halfvec, halfvec) RETURNS float8
CREATE FUNCTION l1_distance(halfvec, halfvec) RETURNS float8 CREATE FUNCTION l1_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME', 'halfvec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'halfvec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_norm(halfvec) RETURNS float8 CREATE FUNCTION l2_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'halfvec_l2_norm' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION quantize_binary(halfvec) RETURNS bit CREATE FUNCTION quantize_binary(halfvec) RETURNS bit
AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -454,7 +454,7 @@ CREATE OPERATOR CLASS halfvec_cosine_ops
FOR TYPE halfvec USING hnsw AS FOR TYPE halfvec USING hnsw AS
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops, OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec), FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
FUNCTION 2 halfvec_norm(halfvec); FUNCTION 2 l2_norm(halfvec);
-- extension casts -- extension casts
@@ -480,6 +480,9 @@ CREATE FUNCTION sparsevec_in(cstring, oid, integer) RETURNS sparsevec
CREATE FUNCTION sparsevec_out(sparsevec) RETURNS cstring CREATE FUNCTION sparsevec_out(sparsevec) RETURNS cstring
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_typmod_in(cstring[]) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_recv(internal, oid, integer) RETURNS sparsevec CREATE FUNCTION sparsevec_recv(internal, oid, integer) RETURNS sparsevec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -489,6 +492,7 @@ CREATE FUNCTION sparsevec_send(sparsevec) RETURNS bytea
CREATE TYPE sparsevec ( CREATE TYPE sparsevec (
INPUT = sparsevec_in, INPUT = sparsevec_in,
OUTPUT = sparsevec_out, OUTPUT = sparsevec_out,
TYPMOD_IN = sparsevec_typmod_in,
RECEIVE = sparsevec_recv, RECEIVE = sparsevec_recv,
SEND = sparsevec_send, SEND = sparsevec_send,
STORAGE = external STORAGE = external
@@ -505,8 +509,8 @@ CREATE FUNCTION inner_product(sparsevec, sparsevec) RETURNS float8
CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8 CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'sparsevec_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_norm(sparsevec) RETURNS float8 CREATE FUNCTION l2_norm(sparsevec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'sparsevec_l2_norm' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- sparsevec private functions -- sparsevec private functions
@@ -571,4 +575,4 @@ CREATE OPERATOR CLASS sparsevec_cosine_ops
FOR TYPE sparsevec USING hnsw AS FOR TYPE sparsevec USING hnsw AS
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops, OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops,
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec), FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
FUNCTION 2 sparsevec_norm(sparsevec); FUNCTION 2 l2_norm(sparsevec);

View File

@@ -950,9 +950,9 @@ halfvec_l1_distance(PG_FUNCTION_ARGS)
/* /*
* Get the L2 norm of a half vector * Get the L2 norm of a half vector
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_norm); PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_norm);
Datum Datum
halfvec_norm(PG_FUNCTION_ARGS) halfvec_l2_norm(PG_FUNCTION_ARGS)
{ {
HalfVector *a = PG_GETARG_HALFVEC_P(0); HalfVector *a = PG_GETARG_HALFVEC_P(0);
half *ax = a->x; half *ax = a->x;

View File

@@ -681,6 +681,8 @@ GetMaxDimensions(HnswType type)
maxDimensions *= 2; maxDimensions *= 2;
else if (type == HNSW_TYPE_BIT) else if (type == HNSW_TYPE_BIT)
maxDimensions *= 32; maxDimensions *= 32;
else if (type == HNSW_TYPE_SPARSEVEC)
maxDimensions = INT_MAX;
return maxDimensions; return maxDimensions;
} }
@@ -691,6 +693,8 @@ GetMaxDimensions(HnswType type)
static void static void
InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo, ForkNumber forkNum) InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo, ForkNumber forkNum)
{ {
int maxDimensions;
buildstate->heap = heap; buildstate->heap = heap;
buildstate->index = index; buildstate->index = index;
buildstate->indexInfo = indexInfo; buildstate->indexInfo = indexInfo;
@@ -701,17 +705,14 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index
buildstate->efConstruction = HnswGetEfConstruction(index); buildstate->efConstruction = HnswGetEfConstruction(index);
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod; buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
maxDimensions = GetMaxDimensions(buildstate->type);
if (buildstate->type != HNSW_TYPE_SPARSEVEC) /* Require column to have dimensions to be indexed */
{ if (buildstate->dimensions < 0)
int maxDimensions = GetMaxDimensions(buildstate->type); elog(ERROR, "column does not have dimensions");
if (buildstate->dimensions < 0) if (buildstate->dimensions > maxDimensions)
elog(ERROR, "column does not have dimensions"); elog(ERROR, "column cannot have more than %d dimensions for hnsw index", maxDimensions);
if (buildstate->dimensions > maxDimensions)
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", maxDimensions);
}
if (buildstate->efConstruction < 2 * buildstate->m) if (buildstate->efConstruction < 2 * buildstate->m)
elog(ERROR, "ef_construction must be greater than or equal to 2 * m"); elog(ERROR, "ef_construction must be greater than or equal to 2 * m");

View File

@@ -229,7 +229,7 @@ HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, HnswType type)
else if (type == HNSW_TYPE_SPARSEVEC) else if (type == HNSW_TYPE_SPARSEVEC)
{ {
SparseVector *v = DatumGetSparseVector(*value); SparseVector *v = DatumGetSparseVector(*value);
SparseVector *result = InitSparseVector(v->nnz); SparseVector *result = InitSparseVector(v->dim, v->nnz);
float *vx = SPARSEVEC_VALUES(v); float *vx = SPARSEVEC_VALUES(v);
float *rx = SPARSEVEC_VALUES(result); float *rx = SPARSEVEC_VALUES(result);

View File

@@ -57,7 +57,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
*/ */
if (buildstate->kmeansnormprocinfo != NULL) if (buildstate->kmeansnormprocinfo != NULL)
{ {
if (!IvfflatNormValue(buildstate->kmeansnormprocinfo, buildstate->collation, &value, buildstate->type)) if (!IvfflatNormValue(buildstate->kmeansnormprocinfo, buildstate->collation, &value))
return; return;
} }
@@ -153,7 +153,7 @@ AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState
/* Normalize if needed */ /* Normalize if needed */
if (buildstate->normprocinfo != NULL) if (buildstate->normprocinfo != NULL)
{ {
if (!IvfflatNormValue(buildstate->normprocinfo, buildstate->collation, &value, buildstate->type)) if (!IvfflatNormValue(buildstate->normprocinfo, buildstate->collation, &value))
return; return;
} }
@@ -312,39 +312,25 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
} }
} }
/*
* Get max dimensions
*/
static int
GetMaxDimensions(IvfflatType type)
{
return IVFFLAT_MAX_DIM;
}
/* /*
* Initialize the build state * Initialize the build state
*/ */
static void static void
InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo) InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo)
{ {
int maxDimensions;
buildstate->heap = heap; buildstate->heap = heap;
buildstate->index = index; buildstate->index = index;
buildstate->indexInfo = indexInfo; buildstate->indexInfo = indexInfo;
buildstate->type = IvfflatGetType(index);
buildstate->lists = IvfflatGetLists(index); buildstate->lists = IvfflatGetLists(index);
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod; buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
maxDimensions = GetMaxDimensions(buildstate->type);
/* Require column to have dimensions to be indexed */ /* Require column to have dimensions to be indexed */
if (buildstate->dimensions < 0) if (buildstate->dimensions < 0)
elog(ERROR, "column does not have dimensions"); elog(ERROR, "column does not have dimensions");
if (buildstate->dimensions > maxDimensions) if (buildstate->dimensions > IVFFLAT_MAX_DIM)
elog(ERROR, "column cannot have more than %d dimensions for ivfflat index", maxDimensions); elog(ERROR, "column cannot have more than %d dimensions for ivfflat index", IVFFLAT_MAX_DIM);
buildstate->reltuples = 0; buildstate->reltuples = 0;
buildstate->indtuples = 0; buildstate->indtuples = 0;

View File

@@ -43,11 +43,6 @@
#define IVFFLAT_MAX_LISTS 32768 #define IVFFLAT_MAX_LISTS 32768
#define IVFFLAT_DEFAULT_PROBES 1 #define IVFFLAT_DEFAULT_PROBES 1
typedef enum IvfflatType
{
IVFFLAT_TYPE_VECTOR
} IvfflatType;
/* Build phases */ /* Build phases */
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */ /* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
#define PROGRESS_IVFFLAT_PHASE_KMEANS 2 #define PROGRESS_IVFFLAT_PHASE_KMEANS 2
@@ -158,7 +153,6 @@ typedef struct IvfflatBuildState
Relation heap; Relation heap;
Relation index; Relation index;
IndexInfo *indexInfo; IndexInfo *indexInfo;
IvfflatType type;
/* Settings */ /* Settings */
int dimensions; int dimensions;
@@ -272,8 +266,7 @@ void VectorArrayFree(VectorArray arr);
void PrintVectorArray(char *msg, VectorArray arr); void PrintVectorArray(char *msg, VectorArray arr);
void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers); void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers);
FmgrInfo *IvfflatOptionalProcInfo(Relation index, uint16 procnum); FmgrInfo *IvfflatOptionalProcInfo(Relation index, uint16 procnum);
IvfflatType IvfflatGetType(Relation index); bool IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value);
bool IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, IvfflatType type);
int IvfflatGetLists(Relation index); int IvfflatGetLists(Relation index);
void IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions); void IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions);
void IvfflatUpdateList(Relation index, ListInfo listInfo, BlockNumber insertPage, BlockNumber originalInsertPage, BlockNumber startPage, ForkNumber forkNum); void IvfflatUpdateList(Relation index, ListInfo listInfo, BlockNumber insertPage, BlockNumber originalInsertPage, BlockNumber startPage, ForkNumber forkNum);

View File

@@ -85,7 +85,7 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, R
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC); normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
if (normprocinfo != NULL) if (normprocinfo != NULL)
{ {
if (!IvfflatNormValue(normprocinfo, index->rd_indcollation[0], &value, IvfflatGetType(index))) if (!IvfflatNormValue(normprocinfo, index->rd_indcollation[0], &value))
return; return;
} }

View File

@@ -268,7 +268,6 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
if (so->first) if (so->first)
{ {
Datum value; Datum value;
IvfflatType type = IvfflatGetType(scan->indexRelation);
/* Count index scan for stats */ /* Count index scan for stats */
pgstat_count_index_scan(scan->indexRelation); pgstat_count_index_scan(scan->indexRelation);
@@ -283,12 +282,7 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
elog(ERROR, "non-MVCC snapshots are not supported with ivfflat"); elog(ERROR, "non-MVCC snapshots are not supported with ivfflat");
if (scan->orderByData->sk_flags & SK_ISNULL) if (scan->orderByData->sk_flags & SK_ISNULL)
{ value = PointerGetDatum(InitVector(so->dimensions));
if (type == IVFFLAT_TYPE_VECTOR)
value = PointerGetDatum(InitVector(so->dimensions));
else
elog(ERROR, "Unsupported type");
}
else else
{ {
value = scan->orderByData->sk_argument; value = scan->orderByData->sk_argument;
@@ -299,7 +293,7 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
/* Fine if normalization fails */ /* Fine if normalization fails */
if (so->normprocinfo != NULL) if (so->normprocinfo != NULL)
IvfflatNormValue(so->normprocinfo, so->collation, &value, type); IvfflatNormValue(so->normprocinfo, so->collation, &value);
} }
IvfflatBench("GetScanLists", GetScanLists(scan, value)); IvfflatBench("GetScanLists", GetScanLists(scan, value));

View File

@@ -66,15 +66,6 @@ IvfflatOptionalProcInfo(Relation index, uint16 procnum)
return index_getprocinfo(index, 1, procnum); return index_getprocinfo(index, 1, procnum);
} }
/*
* Get type
*/
IvfflatType
IvfflatGetType(Relation index)
{
return IVFFLAT_TYPE_VECTOR;
}
/* /*
* Divide by the norm * Divide by the norm
* *
@@ -84,24 +75,19 @@ IvfflatGetType(Relation index)
* if it's different than the original value * if it's different than the original value
*/ */
bool bool
IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, IvfflatType type) IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value)
{ {
double norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value)); double norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value));
if (norm > 0) if (norm > 0)
{ {
if (type == IVFFLAT_TYPE_VECTOR) Vector *v = DatumGetVector(*value);
{ Vector *result = InitVector(v->dim);
Vector *v = DatumGetVector(*value);
Vector *result = InitVector(v->dim);
for (int i = 0; i < v->dim; i++) for (int i = 0; i < v->dim; i++)
result->x[i] = v->x[i] / norm; result->x[i] = v->x[i] / norm;
*value = PointerGetDatum(result); *value = PointerGetDatum(result);
}
else
elog(ERROR, "Unsupported type");
return true; return true;
} }

View File

@@ -18,28 +18,69 @@
#include "utils/builtins.h" #include "utils/builtins.h"
#endif #endif
/*
* Ensure same dimensions
*/
static inline void
CheckDims(SparseVector * a, SparseVector * b)
{
if (a->dim != b->dim)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("different sparsevec dimensions %d and %d", a->dim, b->dim)));
}
/*
* Ensure expected dimensions
*/
static inline void
CheckExpectedDim(int32 typmod, int dim)
{
if (typmod != -1 && typmod != dim)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("expected %d dimensions, not %d", typmod, dim)));
}
/*
* Ensure valid dimensions
*/
static inline void
CheckDim(int dim)
{
if (dim < 1)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("sparsevec must have at least 1 dimension")));
if (dim > SPARSEVEC_MAX_DIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("sparsevec cannot have more than %d dimensions", SPARSEVEC_MAX_DIM)));
}
/* /*
* Ensure valid nnz * Ensure valid nnz
*/ */
static inline void static inline void
CheckNnz(int nnz) CheckNnz(int nnz, int dim)
{ {
if (nnz < 0) if (nnz < 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION), (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("sparsevec must have at least one element"))); errmsg("sparsevec must have at least one element")));
if (nnz > SPARSEVEC_MAX_NNZ) if (nnz > dim)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("sparsevec cannot have more elements than non-zero elements"))); errmsg("sparsevec cannot have more elements than dimensions")));
} }
/* /*
* Ensure valid index * Ensure valid index
*/ */
static inline void static inline void
CheckIndex(int32 *indices, int i) CheckIndex(int32 *indices, int i, int dim)
{ {
int32 index = indices[i]; int32 index = indices[i];
@@ -48,6 +89,11 @@ CheckIndex(int32 *indices, int i)
(errcode(ERRCODE_DATA_EXCEPTION), (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("index must not be negative"))); errmsg("index must not be negative")));
if (index >= dim)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("index must be less than dimensions")));
if (i > 0) if (i > 0)
{ {
if (index < indices[i - 1]) if (index < indices[i - 1])
@@ -83,7 +129,7 @@ CheckElement(float value)
* Allocate and initialize a new sparse vector * Allocate and initialize a new sparse vector
*/ */
SparseVector * SparseVector *
InitSparseVector(int nnz) InitSparseVector(int dim, int nnz)
{ {
SparseVector *result; SparseVector *result;
int size; int size;
@@ -91,6 +137,7 @@ InitSparseVector(int nnz)
size = SPARSEVEC_SIZE(nnz); size = SPARSEVEC_SIZE(nnz);
result = (SparseVector *) palloc0(size); result = (SparseVector *) palloc0(size);
SET_VARSIZE(result, size); SET_VARSIZE(result, size);
result->dim = dim;
result->nnz = nnz; result->nnz = nnz;
return result; return result;
@@ -120,6 +167,8 @@ Datum
sparsevec_in(PG_FUNCTION_ARGS) sparsevec_in(PG_FUNCTION_ARGS)
{ {
char *lit = PG_GETARG_CSTRING(0); char *lit = PG_GETARG_CSTRING(0);
int32 typmod = PG_GETARG_INT32(2);
int dim;
char *pt; char *pt;
char *stringEnd; char *stringEnd;
SparseVector *result; SparseVector *result;
@@ -141,11 +190,6 @@ sparsevec_in(PG_FUNCTION_ARGS)
pt++; pt++;
} }
if (maxNnz > SPARSEVEC_MAX_NNZ)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("sparsevec cannot have more than %d non-zero elements", SPARSEVEC_MAX_NNZ)));
indices = palloc(maxNnz * sizeof(int32)); indices = palloc(maxNnz * sizeof(int32));
values = palloc(maxNnz * sizeof(float)); values = palloc(maxNnz * sizeof(float));
@@ -253,6 +297,24 @@ sparsevec_in(PG_FUNCTION_ARGS)
stringEnd++; stringEnd++;
if (*stringEnd != '/')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed sparsevec literal: \"%s\"", lit),
errdetail("Unexpected end of input.")));
stringEnd++;
/* Use similar logic as int2vectorin */
errno = 0;
pt = stringEnd;
dim = strtol(pt, &stringEnd, 10);
if (stringEnd == pt)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
/* Only whitespace is allowed after the closing brace */ /* Only whitespace is allowed after the closing brace */
while (sparsevec_isspace(*stringEnd)) while (sparsevec_isspace(*stringEnd))
stringEnd++; stringEnd++;
@@ -261,18 +323,21 @@ sparsevec_in(PG_FUNCTION_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed sparsevec literal: \"%s\"", lit), errmsg("malformed sparsevec literal: \"%s\"", lit),
errdetail("Junk after closing right brace."))); errdetail("Junk after closing.")));
pfree(litcopy); pfree(litcopy);
result = InitSparseVector(nnz); CheckDim(dim);
CheckExpectedDim(typmod, dim);
result = InitSparseVector(dim, nnz);
rvalues = SPARSEVEC_VALUES(result); rvalues = SPARSEVEC_VALUES(result);
for (int i = 0; i < nnz; i++) for (int i = 0; i < nnz; i++)
{ {
result->indices[i] = indices[i]; result->indices[i] = indices[i];
rvalues[i] = values[i]; rvalues[i] = values[i];
CheckIndex(result->indices, i); CheckIndex(result->indices, i, dim);
CheckElement(rvalues[i]); CheckElement(rvalues[i]);
} }
@@ -317,9 +382,11 @@ sparsevec_out(PG_FUNCTION_ARGS)
* *
* nnz - 1 bytes for , * nnz - 1 bytes for ,
* *
* 3 bytes for {, }, and \0 * 10 bytes for dimensions
*
* 4 bytes for {, }, /, and \0
*/ */
buf = (char *) palloc((11 + FLOAT_SHORTEST_DECIMAL_LEN) * sparsevec->nnz + 2); buf = (char *) palloc((11 + FLOAT_SHORTEST_DECIMAL_LEN) * sparsevec->nnz + 13);
ptr = buf; ptr = buf;
AppendChar(ptr, '{'); AppendChar(ptr, '{');
@@ -335,12 +402,45 @@ sparsevec_out(PG_FUNCTION_ARGS)
} }
AppendChar(ptr, '}'); AppendChar(ptr, '}');
AppendChar(ptr, '/');
AppendInt(ptr, sparsevec->dim);
*ptr = '\0'; *ptr = '\0';
PG_FREE_IF_COPY(sparsevec, 0); PG_FREE_IF_COPY(sparsevec, 0);
PG_RETURN_CSTRING(buf); PG_RETURN_CSTRING(buf);
} }
/*
* Convert type modifier
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_typmod_in);
Datum
sparsevec_typmod_in(PG_FUNCTION_ARGS)
{
ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
int32 *tl;
int n;
tl = ArrayGetIntegerTypmods(ta, &n);
if (n != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid type modifier")));
if (*tl < 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("dimensions for type sparsevec must be at least 1")));
if (*tl > SPARSEVEC_MAX_DIM)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("dimensions for type sparsevec cannot exceed %d", SPARSEVEC_MAX_DIM)));
PG_RETURN_INT32(*tl);
}
/* /*
* Convert external binary representation to internal representation * Convert external binary representation to internal representation
*/ */
@@ -349,30 +449,33 @@ Datum
sparsevec_recv(PG_FUNCTION_ARGS) sparsevec_recv(PG_FUNCTION_ARGS)
{ {
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
int32 typmod = PG_GETARG_INT32(2);
SparseVector *result; SparseVector *result;
int32 dim;
int32 nnz; int32 nnz;
int32 unused; int32 unused;
int32 unused2;
float *values; float *values;
dim = pq_getmsgint(buf, sizeof(int32));
nnz = pq_getmsgint(buf, sizeof(int32)); nnz = pq_getmsgint(buf, sizeof(int32));
unused = pq_getmsgint(buf, sizeof(int32)); unused = pq_getmsgint(buf, sizeof(int32));
unused2 = pq_getmsgint(buf, sizeof(int32));
CheckNnz(nnz); CheckDim(dim);
CheckNnz(nnz, dim);
CheckExpectedDim(typmod, dim);
if (unused != 0 || unused2 != 0) if (unused != 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION), (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("expected unused to be 0, not %d", unused))); errmsg("expected unused to be 0, not %d", unused)));
result = InitSparseVector(nnz); result = InitSparseVector(dim, nnz);
values = SPARSEVEC_VALUES(result); values = SPARSEVEC_VALUES(result);
for (int i = 0; i < nnz; i++) for (int i = 0; i < nnz; i++)
{ {
result->indices[i] = pq_getmsgint(buf, sizeof(int32)); result->indices[i] = pq_getmsgint(buf, sizeof(int32));
CheckIndex(result->indices, i); CheckIndex(result->indices, i, dim);
} }
for (int i = 0; i < nnz; i++) for (int i = 0; i < nnz; i++)
@@ -396,9 +499,9 @@ sparsevec_send(PG_FUNCTION_ARGS)
StringInfoData buf; StringInfoData buf;
pq_begintypsend(&buf); pq_begintypsend(&buf);
pq_sendint(&buf, svec->dim, sizeof(int32));
pq_sendint(&buf, svec->nnz, sizeof(int32)); pq_sendint(&buf, svec->nnz, sizeof(int32));
pq_sendint(&buf, svec->unused, sizeof(int32)); pq_sendint(&buf, svec->unused, sizeof(int32));
pq_sendint(&buf, svec->unused2, sizeof(int32));
for (int i = 0; i < svec->nnz; i++) for (int i = 0; i < svec->nnz; i++)
pq_sendint(&buf, svec->indices[i], sizeof(int32)); pq_sendint(&buf, svec->indices[i], sizeof(int32));
for (int i = 0; i < svec->nnz; i++) for (int i = 0; i < svec->nnz; i++)
@@ -409,12 +512,16 @@ sparsevec_send(PG_FUNCTION_ARGS)
/* /*
* Convert sparse vector to sparse vector * Convert sparse vector to sparse vector
* This is needed to check the type modifier
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec); PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec);
Datum Datum
sparsevec(PG_FUNCTION_ARGS) sparsevec(PG_FUNCTION_ARGS)
{ {
SparseVector *svec = PG_GETARG_SPARSEVEC_P(0); SparseVector *svec = PG_GETARG_SPARSEVEC_P(0);
int32 typmod = PG_GETARG_INT32(1);
CheckExpectedDim(typmod, svec->dim);
PG_RETURN_POINTER(svec); PG_RETURN_POINTER(svec);
} }
@@ -427,20 +534,23 @@ Datum
vector_to_sparsevec(PG_FUNCTION_ARGS) vector_to_sparsevec(PG_FUNCTION_ARGS)
{ {
Vector *vec = PG_GETARG_VECTOR_P(0); Vector *vec = PG_GETARG_VECTOR_P(0);
int32 typmod = PG_GETARG_INT32(1);
SparseVector *result; SparseVector *result;
int dim = vec->dim; int dim = vec->dim;
int nnz = 0; int nnz = 0;
float *values; float *values;
int j = 0; int j = 0;
CheckDim(dim);
CheckExpectedDim(typmod, dim);
for (int i = 0; i < dim; i++) for (int i = 0; i < dim; i++)
{ {
if (vec->x[i] != 0) if (vec->x[i] != 0)
nnz++; nnz++;
} }
CheckNnz(nnz); result = InitSparseVector(dim, nnz);
result = InitSparseVector(nnz);
values = SPARSEVEC_VALUES(result); values = SPARSEVEC_VALUES(result);
for (int i = 0; i < dim; i++) for (int i = 0; i < dim; i++)
{ {
@@ -517,6 +627,8 @@ sparsevec_l2_distance(PG_FUNCTION_ARGS)
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1); SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
CheckDims(a, b);
PG_RETURN_FLOAT8(sqrt(l2_distance_squared_internal(a, b))); PG_RETURN_FLOAT8(sqrt(l2_distance_squared_internal(a, b)));
} }
@@ -531,6 +643,8 @@ sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1); SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
CheckDims(a, b);
PG_RETURN_FLOAT8(l2_distance_squared_internal(a, b)); PG_RETURN_FLOAT8(l2_distance_squared_internal(a, b));
} }
@@ -580,6 +694,8 @@ sparsevec_inner_product(PG_FUNCTION_ARGS)
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1); SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
CheckDims(a, b);
PG_RETURN_FLOAT8(inner_product_internal(a, b)); PG_RETURN_FLOAT8(inner_product_internal(a, b));
} }
@@ -593,6 +709,8 @@ sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1); SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
CheckDims(a, b);
PG_RETURN_FLOAT8(-inner_product_internal(a, b)); PG_RETURN_FLOAT8(-inner_product_internal(a, b));
} }
@@ -611,6 +729,8 @@ sparsevec_cosine_distance(PG_FUNCTION_ARGS)
float normb = 0.0; float normb = 0.0;
double similarity; double similarity;
CheckDims(a, b);
similarity = inner_product_internal(a, b); similarity = inner_product_internal(a, b);
/* Auto-vectorized */ /* Auto-vectorized */
@@ -642,9 +762,9 @@ sparsevec_cosine_distance(PG_FUNCTION_ARGS)
/* /*
* Get the L2 norm of a sparse vector * Get the L2 norm of a sparse vector
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_norm); PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_norm);
Datum Datum
sparsevec_norm(PG_FUNCTION_ARGS) sparsevec_l2_norm(PG_FUNCTION_ARGS)
{ {
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
float *ax = SPARSEVEC_VALUES(a); float *ax = SPARSEVEC_VALUES(a);

View File

@@ -1,7 +1,7 @@
#ifndef SPARSEVEC_H #ifndef SPARSEVEC_H
#define SPARSEVEC_H #define SPARSEVEC_H
#define SPARSEVEC_MAX_NNZ 100000 #define SPARSEVEC_MAX_DIM 100000
/* Ensure values are aligned */ /* Ensure values are aligned */
#define SPARSEVEC_SIZE(_nnz) (offsetof(SparseVector, indices) + MAXALIGN((_nnz) * sizeof(int32)) + (_nnz * sizeof(float))) #define SPARSEVEC_SIZE(_nnz) (offsetof(SparseVector, indices) + MAXALIGN((_nnz) * sizeof(int32)) + (_nnz * sizeof(float)))
@@ -13,12 +13,12 @@
typedef struct SparseVector typedef struct SparseVector
{ {
int32 vl_len_; /* varlena header (do not touch directly!) */ int32 vl_len_; /* varlena header (do not touch directly!) */
int32 dim; /* number of dimensions */
int32 nnz; int32 nnz;
int32 unused; int32 unused;
int32 unused2;
int32 indices[FLEXIBLE_ARRAY_MEMBER]; int32 indices[FLEXIBLE_ARRAY_MEMBER];
} SparseVector; } SparseVector;
SparseVector *InitSparseVector(int nnz); SparseVector *InitSparseVector(int dim, int nnz);
#endif #endif

View File

@@ -1236,19 +1236,11 @@ sparsevec_to_vector(PG_FUNCTION_ARGS)
SparseVector *svec = PG_GETARG_SPARSEVEC_P(0); SparseVector *svec = PG_GETARG_SPARSEVEC_P(0);
int32 typmod = PG_GETARG_INT32(1); int32 typmod = PG_GETARG_INT32(1);
Vector *result; Vector *result;
int dim; int dim = svec->dim;
float *values = SPARSEVEC_VALUES(svec); float *values = SPARSEVEC_VALUES(svec);
int maxIndex = svec->nnz == 0 ? -1 : svec->indices[svec->nnz - 1];
if (typmod == -1)
dim = maxIndex + 1;
else
dim = typmod;
CheckDim(dim); CheckDim(dim);
CheckExpectedDim(typmod, dim);
if (dim < maxIndex + 1)
elog(ERROR, "Vector must have at least %d dimensions", maxIndex + 1);
result = InitVector(dim); result = InitVector(dim);
for (int i = 0; i < svec->nnz; i++) for (int i = 0; i < svec->nnz; i++)

View File

@@ -1,17 +1,17 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec); CREATE TABLE t (val sparsevec(3));
INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL); INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops); CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3');
SELECT * FROM t ORDER BY val <=> '{0:3,1:3,2:3}'; SELECT * FROM t ORDER BY val <=> '{0:3,1:3,2:3}/3';
val val
--------------- -----------------
{0:1,1:1,2:1} {0:1,1:1,2:1}/3
{0:1,1:2,2:3} {0:1,1:2,2:3}/3
{0:1,1:2,2:4} {0:1,1:2,2:4}/3
(3 rows) (3 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}') t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}/3') t2;
count count
------- -------
3 3

View File

@@ -1,15 +1,15 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec); CREATE TABLE t (val sparsevec(3));
INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL); INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops); CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3');
SELECT * FROM t ORDER BY val <#> '{0:3,1:3,2:3}'; SELECT * FROM t ORDER BY val <#> '{0:3,1:3,2:3}/3';
val val
--------------- -----------------
{0:1,1:2,2:4} {0:1,1:2,2:4}/3
{0:1,1:2,2:3} {0:1,1:2,2:3}/3
{0:1,1:1,2:1} {0:1,1:1,2:1}/3
{} {}/3
(4 rows) (4 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2;

View File

@@ -1,15 +1,15 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec); CREATE TABLE t (val sparsevec(3));
INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL); INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops); CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3');
SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}'; SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}/3';
val val
--------------- -----------------
{0:1,1:2,2:3} {0:1,1:2,2:3}/3
{0:1,1:2,2:4} {0:1,1:2,2:4}/3
{0:1,1:1,2:1} {0:1,1:1,2:1}/3
{} {}/3
(4 rows) (4 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2;
@@ -25,14 +25,14 @@ SELECT COUNT(*) FROM t;
(1 row) (1 row)
TRUNCATE t; TRUNCATE t;
SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}'; SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}/3';
val val
----- -----
(0 rows) (0 rows)
DROP TABLE t; DROP TABLE t;
-- TODO move -- TODO move
CREATE TABLE t (val sparsevec); CREATE TABLE t (val sparsevec(1001));
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec); INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops); CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index

View File

@@ -1,60 +1,62 @@
SELECT l2_distance('{}'::sparsevec, '{0:3,1:4}'); SELECT l2_distance('{}/2'::sparsevec, '{0:3,1:4}/2');
l2_distance l2_distance
------------- -------------
5 5
(1 row) (1 row)
SELECT l2_distance('{}'::sparsevec, '{1:1}'); SELECT l2_distance('{}/2'::sparsevec, '{1:1}/2');
l2_distance l2_distance
------------- -------------
1 1
(1 row) (1 row)
SELECT '{}'::sparsevec <-> '{0:3,1:4}'; SELECT '{}/2'::sparsevec <-> '{0:3,1:4}/2';
?column? ?column?
---------- ----------
5 5
(1 row) (1 row)
SELECT inner_product('{0:1,1:2}'::sparsevec, '{0:2,1:4}'); SELECT inner_product('{0:1,1:2}/2'::sparsevec, '{0:2,1:4}/2');
inner_product inner_product
--------------- ---------------
10 10
(1 row) (1 row)
SELECT sparsevec_negative_inner_product('{0:1,1:2}', '{0:2,1:4}'); SELECT sparsevec_negative_inner_product('{0:1,1:2}/2', '{0:2,1:4}/2');
sparsevec_negative_inner_product sparsevec_negative_inner_product
---------------------------------- ----------------------------------
-10 -10
(1 row) (1 row)
SELECT cosine_distance('{0:1,1:2}'::sparsevec, '{0:2,1:4}'); SELECT cosine_distance('{0:1,1:2}/2'::sparsevec, '{0:2,1:4}/2');
cosine_distance cosine_distance
----------------- -----------------
0 0
(1 row) (1 row)
SELECT cosine_distance('{0:1,1:2}'::sparsevec, '{}'); SELECT cosine_distance('{0:1,1:2}/2'::sparsevec, '{}/2');
cosine_distance cosine_distance
----------------- -----------------
NaN NaN
(1 row) (1 row)
SELECT cosine_distance('{0:1,1:1}'::sparsevec, '{0:-1,1:-1}'); SELECT cosine_distance('{0:1,1:1}/2'::sparsevec, '{0:-1,1:-1}/2');
cosine_distance cosine_distance
----------------- -----------------
2 2
(1 row) (1 row)
SELECT cosine_distance('{0:1}'::sparsevec, '{1:2}'); SELECT cosine_distance('{0:1}/2'::sparsevec, '{1:2}/2');
cosine_distance cosine_distance
----------------- -----------------
1 1
(1 row) (1 row)
SELECT cosine_distance('{}'::sparsevec, '{}'); SELECT cosine_distance('{}/1'::sparsevec, '{}/1');
cosine_distance cosine_distance
----------------- -----------------
NaN NaN
(1 row) (1 row)
SELECT cosine_distance('{0:1}/2'::sparsevec, '{0:1}/3');
ERROR: different sparsevec dimensions 2 and 3

View File

@@ -1,64 +1,62 @@
SELECT '{0:1.5,2:3.5}'::sparsevec; SELECT '{0:1.5,2:3.5}/5'::sparsevec;
sparsevec sparsevec
--------------- -----------------
{0:1.5,2:3.5} {0:1.5,2:3.5}/5
(1 row) (1 row)
SELECT '{0:1.5,2:3.5}'::sparsevec::vector; SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector;
vector
-------------
[1.5,0,3.5]
(1 row)
SELECT '{0:1.5,2:3.5}'::sparsevec::vector(5);
vector vector
----------------- -----------------
[1.5,0,3.5,0,0] [1.5,0,3.5,0,0]
(1 row) (1 row)
SELECT '{0:1.5,2:3.5}'::sparsevec::vector(4); SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector(5);
vector vector
--------------- -----------------
[1.5,0,3.5,0] [1.5,0,3.5,0,0]
(1 row) (1 row)
SELECT '{0:1.5,2:3.5}'::sparsevec::vector(2); SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector(4);
ERROR: Vector must have at least 3 dimensions ERROR: expected 4 dimensions, not 5
SELECT '[0,1.5,0,3.5,0]'::vector::sparsevec; SELECT '[0,1.5,0,3.5,0]'::vector::sparsevec;
sparsevec sparsevec
--------------- -----------------
{1:1.5,3:3.5} {1:1.5,3:3.5}/5
(1 row) (1 row)
SELECT '{0:0,1:1,2:0}'::sparsevec; SELECT '{0:0,1:1,2:0}/3'::sparsevec;
sparsevec sparsevec
----------- -----------
{1:1} {1:1}/3
(1 row) (1 row)
SELECT '{1:1,0:1}'::sparsevec; SELECT '{1:1,0:1}/2'::sparsevec;
ERROR: indexes must be in ascending order ERROR: indexes must be in ascending order
LINE 1: SELECT '{1:1,0:1}'::sparsevec; LINE 1: SELECT '{1:1,0:1}/2'::sparsevec;
^ ^
SELECT '{}'::sparsevec; SELECT '{}/5'::sparsevec;
sparsevec sparsevec
----------- -----------
{} {}/5
(1 row) (1 row)
SELECT '{}'::sparsevec::vector; SELECT '{}/-1'::sparsevec;
ERROR: vector must have at least 1 dimension ERROR: sparsevec must have at least 1 dimension
SELECT '{-1:1}'::sparsevec; LINE 1: SELECT '{}/-1'::sparsevec;
^
SELECT '{}/100001'::sparsevec;
ERROR: sparsevec cannot have more than 100000 dimensions
LINE 1: SELECT '{}/100001'::sparsevec;
^
SELECT '{}/16001'::sparsevec::vector;
ERROR: vector cannot have more than 16000 dimensions
SELECT '{-1:1}/1'::sparsevec;
ERROR: index "-1" is out of range for type sparsevec ERROR: index "-1" is out of range for type sparsevec
LINE 1: SELECT '{-1:1}'::sparsevec; LINE 1: SELECT '{-1:1}/1'::sparsevec;
^ ^
SELECT '{1:1}'::sparsevec; SELECT '{1:1}/1'::sparsevec;
sparsevec ERROR: index must be less than dimensions
----------- LINE 1: SELECT '{1:1}/1'::sparsevec;
{1:1} ^
(1 row) SELECT '{}/1'::sparsevec(2);
ERROR: expected 2 dimensions, not 1
SELECT '{}'::sparsevec(2);
ERROR: type modifier is not allowed for type "sparsevec"
LINE 1: SELECT '{}'::sparsevec(2);
^

View File

@@ -1,13 +1,13 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec); CREATE TABLE t (val sparsevec(3));
INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL); INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops); CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3');
SELECT * FROM t ORDER BY val <=> '{0:3,1:3,2:3}'; SELECT * FROM t ORDER BY val <=> '{0:3,1:3,2:3}/3';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}') t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}/3') t2;
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::sparsevec)) t2;
DROP TABLE t; DROP TABLE t;

View File

@@ -1,12 +1,12 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec); CREATE TABLE t (val sparsevec(3));
INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL); INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops); CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3');
SELECT * FROM t ORDER BY val <#> '{0:3,1:3,2:3}'; SELECT * FROM t ORDER BY val <#> '{0:3,1:3,2:3}/3';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2;
DROP TABLE t; DROP TABLE t;

View File

@@ -1,22 +1,22 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec); CREATE TABLE t (val sparsevec(3));
INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL); INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops); CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3');
SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}'; SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}/3';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2;
SELECT COUNT(*) FROM t; SELECT COUNT(*) FROM t;
TRUNCATE t; TRUNCATE t;
SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}'; SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}/3';
DROP TABLE t; DROP TABLE t;
-- TODO move -- TODO move
CREATE TABLE t (val sparsevec); CREATE TABLE t (val sparsevec(1001));
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec); INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops); CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
TRUNCATE t; TRUNCATE t;

View File

@@ -1,12 +1,13 @@
SELECT l2_distance('{}'::sparsevec, '{0:3,1:4}'); SELECT l2_distance('{}/2'::sparsevec, '{0:3,1:4}/2');
SELECT l2_distance('{}'::sparsevec, '{1:1}'); SELECT l2_distance('{}/2'::sparsevec, '{1:1}/2');
SELECT '{}'::sparsevec <-> '{0:3,1:4}'; SELECT '{}/2'::sparsevec <-> '{0:3,1:4}/2';
SELECT inner_product('{0:1,1:2}'::sparsevec, '{0:2,1:4}'); SELECT inner_product('{0:1,1:2}/2'::sparsevec, '{0:2,1:4}/2');
SELECT sparsevec_negative_inner_product('{0:1,1:2}', '{0:2,1:4}'); SELECT sparsevec_negative_inner_product('{0:1,1:2}/2', '{0:2,1:4}/2');
SELECT cosine_distance('{0:1,1:2}'::sparsevec, '{0:2,1:4}'); SELECT cosine_distance('{0:1,1:2}/2'::sparsevec, '{0:2,1:4}/2');
SELECT cosine_distance('{0:1,1:2}'::sparsevec, '{}'); SELECT cosine_distance('{0:1,1:2}/2'::sparsevec, '{}/2');
SELECT cosine_distance('{0:1,1:1}'::sparsevec, '{0:-1,1:-1}'); SELECT cosine_distance('{0:1,1:1}/2'::sparsevec, '{0:-1,1:-1}/2');
SELECT cosine_distance('{0:1}'::sparsevec, '{1:2}'); SELECT cosine_distance('{0:1}/2'::sparsevec, '{1:2}/2');
SELECT cosine_distance('{}'::sparsevec, '{}'); SELECT cosine_distance('{}/1'::sparsevec, '{}/1');
SELECT cosine_distance('{0:1}/2'::sparsevec, '{0:1}/3');

View File

@@ -1,18 +1,19 @@
SELECT '{0:1.5,2:3.5}'::sparsevec; SELECT '{0:1.5,2:3.5}/5'::sparsevec;
SELECT '{0:1.5,2:3.5}'::sparsevec::vector; SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector;
SELECT '{0:1.5,2:3.5}'::sparsevec::vector(5); SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector(5);
SELECT '{0:1.5,2:3.5}'::sparsevec::vector(4); SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector(4);
SELECT '{0:1.5,2:3.5}'::sparsevec::vector(2);
SELECT '[0,1.5,0,3.5,0]'::vector::sparsevec; SELECT '[0,1.5,0,3.5,0]'::vector::sparsevec;
SELECT '{0:0,1:1,2:0}'::sparsevec; SELECT '{0:0,1:1,2:0}/3'::sparsevec;
SELECT '{1:1,0:1}'::sparsevec; SELECT '{1:1,0:1}/2'::sparsevec;
SELECT '{}'::sparsevec; SELECT '{}/5'::sparsevec;
SELECT '{}'::sparsevec::vector; SELECT '{}/-1'::sparsevec;
SELECT '{}/100001'::sparsevec;
SELECT '{}/16001'::sparsevec::vector;
SELECT '{-1:1}'::sparsevec; SELECT '{-1:1}/1'::sparsevec;
SELECT '{1:1}'::sparsevec; SELECT '{1:1}/1'::sparsevec;
SELECT '{}'::sparsevec(2); SELECT '{}/1'::sparsevec(2);