Compare commits

...

7 Commits

Author SHA1 Message Date
Andrew Kane
a5bb59d9f6 Use normalize_l2 for ivfflat 2023-10-16 17:56:50 -07:00
Andrew Kane
dd609f200b Merge branch 'normalize_l2' into hnsw-normalize 2023-10-16 17:09:20 -07:00
Andrew Kane
9514c152bc Fixed precision on Windows 2023-10-16 17:08:19 -07:00
Andrew Kane
b391e40765 Always normalize 2023-10-16 16:53:40 -07:00
Andrew Kane
0054a9c40a Use normalize_l2 for normalization 2023-10-16 16:42:40 -07:00
Andrew Kane
9ed7e63fb7 Prevent overflow at cost to speed 2023-08-09 12:36:29 -07:00
Andrew Kane
47e361a93d Added normalize_l2 function 2023-08-09 11:29:14 -07:00
21 changed files with 160 additions and 63 deletions

View File

@@ -3,6 +3,7 @@
- Added HNSW index type - Added HNSW index type
- Added support for parallel index builds - Added support for parallel index builds
- Added `l1_distance` function - Added `l1_distance` function
- Added `normalize_l2` function
- Added element-wise multiplication for vectors - Added element-wise multiplication for vectors
- Added `sum` aggregate - Added `sum` aggregate
- Improved performance of distance functions - Improved performance of distance functions

View File

@@ -392,6 +392,7 @@ cosine_distance(vector, vector) → double precision | cosine distance
inner_product(vector, vector) → double precision | inner product inner_product(vector, vector) → double precision | inner product
l2_distance(vector, vector) → double precision | Euclidean distance l2_distance(vector, vector) → double precision | Euclidean distance
l1_distance(vector, vector) → double precision | taxicab distance [unreleased] l1_distance(vector, vector) → double precision | taxicab distance [unreleased]
normalize_l2(vector) → vector | normalize with Euclidean norm [unreleased]
vector_dims(vector) → integer | number of dimensions vector_dims(vector) → integer | number of dimensions
vector_norm(vector) → double precision | Euclidean norm vector_norm(vector) → double precision | Euclidean norm

View File

@@ -4,6 +4,9 @@
CREATE FUNCTION l1_distance(vector, vector) RETURNS float8 CREATE FUNCTION l1_distance(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION normalize_l2(vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_mul(vector, vector) RETURNS vector CREATE FUNCTION vector_mul(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

View File

@@ -49,6 +49,9 @@ CREATE FUNCTION vector_dims(vector) RETURNS integer
CREATE FUNCTION vector_norm(vector) RETURNS float8 CREATE FUNCTION vector_norm(vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION normalize_l2(vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_add(vector, vector) RETURNS vector CREATE FUNCTION vector_add(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -265,15 +268,15 @@ CREATE OPERATOR CLASS vector_ip_ops
OPERATOR 1 <#> (vector, vector) FOR ORDER BY float_ops, OPERATOR 1 <#> (vector, vector) FOR ORDER BY float_ops,
FUNCTION 1 vector_negative_inner_product(vector, vector), FUNCTION 1 vector_negative_inner_product(vector, vector),
FUNCTION 3 vector_spherical_distance(vector, vector), FUNCTION 3 vector_spherical_distance(vector, vector),
FUNCTION 4 vector_norm(vector); FUNCTION 6 normalize_l2(vector);
CREATE OPERATOR CLASS vector_cosine_ops CREATE OPERATOR CLASS vector_cosine_ops
FOR TYPE vector USING ivfflat AS FOR TYPE vector USING ivfflat AS
OPERATOR 1 <=> (vector, vector) FOR ORDER BY float_ops, OPERATOR 1 <=> (vector, vector) FOR ORDER BY float_ops,
FUNCTION 1 vector_negative_inner_product(vector, vector), FUNCTION 1 vector_negative_inner_product(vector, vector),
FUNCTION 2 vector_norm(vector),
FUNCTION 3 vector_spherical_distance(vector, vector), FUNCTION 3 vector_spherical_distance(vector, vector),
FUNCTION 4 vector_norm(vector); FUNCTION 5 normalize_l2(vector),
FUNCTION 6 normalize_l2(vector);
CREATE OPERATOR CLASS vector_l2_ops CREATE OPERATOR CLASS vector_l2_ops
FOR TYPE vector USING hnsw AS FOR TYPE vector USING hnsw AS
@@ -289,4 +292,4 @@ CREATE OPERATOR CLASS vector_cosine_ops
FOR TYPE vector USING hnsw AS FOR TYPE vector USING hnsw AS
OPERATOR 1 <=> (vector, vector) FOR ORDER BY float_ops, OPERATOR 1 <=> (vector, vector) FOR ORDER BY float_ops,
FUNCTION 1 vector_negative_inner_product(vector, vector), FUNCTION 1 vector_negative_inner_product(vector, vector),
FUNCTION 2 vector_norm(vector); FUNCTION 3 normalize_l2(vector);

View File

@@ -167,7 +167,7 @@ hnswhandler(PG_FUNCTION_ARGS)
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine); IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
amroutine->amstrategies = 0; amroutine->amstrategies = 0;
amroutine->amsupport = 2; amroutine->amsupport = 3;
#if PG_VERSION_NUM >= 130000 #if PG_VERSION_NUM >= 130000
amroutine->amoptsprocnum = 0; amroutine->amoptsprocnum = 0;
#endif #endif

View File

@@ -19,6 +19,7 @@
/* Support functions */ /* Support functions */
#define HNSW_DISTANCE_PROC 1 #define HNSW_DISTANCE_PROC 1
#define HNSW_NORM_PROC 2 #define HNSW_NORM_PROC 2
#define HNSW_NORMALIZE_PROC 3
#define HNSW_VERSION 1 #define HNSW_VERSION 1
#define HNSW_MAGIC_NUMBER 0xA953A953 #define HNSW_MAGIC_NUMBER 0xA953A953
@@ -147,6 +148,7 @@ typedef struct HnswBuildState
/* Support functions */ /* Support functions */
FmgrInfo *procinfo; FmgrInfo *procinfo;
FmgrInfo *normprocinfo; FmgrInfo *normprocinfo;
FmgrInfo *normalizeprocinfo;
Oid collation; Oid collation;
/* Variables */ /* Variables */
@@ -220,6 +222,7 @@ typedef struct HnswScanOpaqueData
/* Support functions */ /* Support functions */
FmgrInfo *procinfo; FmgrInfo *procinfo;
FmgrInfo *normprocinfo; FmgrInfo *normprocinfo;
FmgrInfo *normalizeprocinfo;
Oid collation; Oid collation;
} HnswScanOpaqueData; } HnswScanOpaqueData;
@@ -255,7 +258,7 @@ typedef struct HnswVacuumState
int HnswGetM(Relation index); int HnswGetM(Relation index);
int HnswGetEfConstruction(Relation index); int HnswGetEfConstruction(Relation index);
FmgrInfo *HnswOptionalProcInfo(Relation rel, uint16 procnum); FmgrInfo *HnswOptionalProcInfo(Relation rel, uint16 procnum);
bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result); void HnswNormValue(FmgrInfo *procinfo, FmgrInfo *normalizeprocinfo, Oid collation, Datum *value, Vector * result);
void HnswCommitBuffer(Buffer buf, GenericXLogState *state); void HnswCommitBuffer(Buffer buf, GenericXLogState *state);
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum); Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
void HnswInitPage(Buffer buf, Page page); void HnswInitPage(Buffer buf, Page page);

View File

@@ -278,11 +278,7 @@ InsertTuple(Relation index, Datum *values, HnswElement element, HnswBuildState *
Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0])); Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
/* Normalize if needed */ /* Normalize if needed */
if (buildstate->normprocinfo != NULL) HnswNormValue(buildstate->normprocinfo, buildstate->normalizeprocinfo, collation, &value, buildstate->normvec);
{
if (!HnswNormValue(buildstate->normprocinfo, collation, &value, buildstate->normvec))
return false;
}
/* Copy value to element so accessible outside of memory context */ /* Copy value to element so accessible outside of memory context */
memcpy(element->vec, DatumGetVector(value), VECTOR_SIZE(buildstate->dimensions)); memcpy(element->vec, DatumGetVector(value), VECTOR_SIZE(buildstate->dimensions));
@@ -413,6 +409,7 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index
/* Get support functions */ /* Get support functions */
buildstate->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC); buildstate->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC);
buildstate->normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC); buildstate->normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
buildstate->normalizeprocinfo = HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC);
buildstate->collation = index->rd_indcollation[0]; buildstate->collation = index->rd_indcollation[0];
buildstate->elements = NIL; buildstate->elements = NIL;

View File

@@ -417,6 +417,7 @@ HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_ti
{ {
Datum value; Datum value;
FmgrInfo *normprocinfo; FmgrInfo *normprocinfo;
FmgrInfo *normalizeprocinfo;
HnswElement entryPoint; HnswElement entryPoint;
HnswElement element; HnswElement element;
int m = HnswGetM(index); int m = HnswGetM(index);
@@ -432,11 +433,8 @@ HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_ti
/* Normalize if needed */ /* Normalize if needed */
normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC); normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
if (normprocinfo != NULL) normalizeprocinfo = HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC);
{ HnswNormValue(normprocinfo, normalizeprocinfo, collation, &value, NULL);
if (!HnswNormValue(normprocinfo, collation, &value, NULL))
return false;
}
/* Create an element */ /* Create an element */
element = HnswInitElement(heap_tid, m, ml, HnswGetMaxLevel(m)); element = HnswInitElement(heap_tid, m, ml, HnswGetMaxLevel(m));

View File

@@ -78,6 +78,7 @@ hnswbeginscan(Relation index, int nkeys, int norderbys)
/* Set support functions */ /* Set support functions */
so->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC); so->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC);
so->normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC); so->normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
so->normalizeprocinfo = HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC);
so->collation = index->rd_indcollation[0]; so->collation = index->rd_indcollation[0];
scan->opaque = so; scan->opaque = so;
@@ -140,8 +141,7 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value))); Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
/* Fine if normalization fails */ /* Fine if normalization fails */
if (so->normprocinfo != NULL) HnswNormValue(so->normprocinfo, so->normalizeprocinfo, so->collation, &value, NULL);
HnswNormValue(so->normprocinfo, so->collation, &value, NULL);
} }
GetScanItems(scan, value); GetScanItems(scan, value);

View File

@@ -47,17 +47,26 @@ HnswOptionalProcInfo(Relation rel, uint16 procnum)
} }
/* /*
* Divide by the norm * Normalize a vector
*
* Returns false if value should not be indexed
* *
* The caller needs to free the pointer stored in value * The caller needs to free the pointer stored in value
* if it's different than the original value * if it's different than the original value
*/ */
bool void
HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result) HnswNormValue(FmgrInfo *procinfo, FmgrInfo *normalizeprocinfo, Oid collation, Datum *value, Vector * result)
{ {
double norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value)); double norm;
if (normalizeprocinfo != NULL)
{
*value = FunctionCall1Coll(normalizeprocinfo, collation, *value);
return;
}
if (procinfo == NULL)
return;
norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value));
if (norm > 0) if (norm > 0)
{ {
@@ -70,11 +79,7 @@ HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result)
result->x[i] = v->x[i] / norm; result->x[i] = v->x[i] / norm;
*value = PointerGetDatum(result); *value = PointerGetDatum(result);
return true;
} }
return false;
} }
/* /*

View File

@@ -75,11 +75,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
* Normalize with KMEANS_NORM_PROC since spherical distance function * Normalize with KMEANS_NORM_PROC since spherical distance function
* expects unit vectors * expects unit vectors
*/ */
if (buildstate->kmeansnormprocinfo != NULL) IvfflatNormValue(buildstate->kmeansnormprocinfo, buildstate->kmeansnormalizeprocinfo, buildstate->collation, &value, buildstate->normvec);
{
if (!IvfflatNormValue(buildstate->kmeansnormprocinfo, buildstate->collation, &value, buildstate->normvec))
return;
}
if (samples->length < targsamples) if (samples->length < targsamples)
{ {
@@ -176,11 +172,7 @@ AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState
Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0])); Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
/* Normalize if needed */ /* Normalize if needed */
if (buildstate->normprocinfo != NULL) IvfflatNormValue(buildstate->normprocinfo, buildstate->normalizeprocinfo, buildstate->collation, &value, buildstate->normvec);
{
if (!IvfflatNormValue(buildstate->normprocinfo, buildstate->collation, &value, buildstate->normvec))
return;
}
/* Find the list that minimizes the distance */ /* Find the list that minimizes the distance */
for (int i = 0; i < centers->length; i++) for (int i = 0; i < centers->length; i++)
@@ -368,6 +360,8 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
buildstate->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC); buildstate->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC);
buildstate->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC); buildstate->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
buildstate->kmeansnormprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC); buildstate->kmeansnormprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
buildstate->normalizeprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC);
buildstate->kmeansnormalizeprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORMALIZE_PROC);
buildstate->collation = index->rd_indcollation[0]; buildstate->collation = index->rd_indcollation[0];
/* Require more than one dimension for spherical k-means */ /* Require more than one dimension for spherical k-means */

View File

@@ -194,7 +194,7 @@ ivfflathandler(PG_FUNCTION_ARGS)
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine); IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
amroutine->amstrategies = 0; amroutine->amstrategies = 0;
amroutine->amsupport = 4; amroutine->amsupport = 6;
#if PG_VERSION_NUM >= 130000 #if PG_VERSION_NUM >= 130000
amroutine->amoptsprocnum = 0; amroutine->amoptsprocnum = 0;
#endif #endif

View File

@@ -31,6 +31,8 @@
#define IVFFLAT_NORM_PROC 2 #define IVFFLAT_NORM_PROC 2
#define IVFFLAT_KMEANS_DISTANCE_PROC 3 #define IVFFLAT_KMEANS_DISTANCE_PROC 3
#define IVFFLAT_KMEANS_NORM_PROC 4 #define IVFFLAT_KMEANS_NORM_PROC 4
#define IVFFLAT_NORMALIZE_PROC 5
#define IVFFLAT_KMEANS_NORMALIZE_PROC 6
#define IVFFLAT_VERSION 1 #define IVFFLAT_VERSION 1
#define IVFFLAT_MAGIC_NUMBER 0x14FF1A7 #define IVFFLAT_MAGIC_NUMBER 0x14FF1A7
@@ -172,6 +174,8 @@ typedef struct IvfflatBuildState
FmgrInfo *procinfo; FmgrInfo *procinfo;
FmgrInfo *normprocinfo; FmgrInfo *normprocinfo;
FmgrInfo *kmeansnormprocinfo; FmgrInfo *kmeansnormprocinfo;
FmgrInfo *normalizeprocinfo;
FmgrInfo *kmeansnormalizeprocinfo;
Oid collation; Oid collation;
/* Variables */ /* Variables */
@@ -253,6 +257,7 @@ typedef struct IvfflatScanOpaqueData
/* Support functions */ /* Support functions */
FmgrInfo *procinfo; FmgrInfo *procinfo;
FmgrInfo *normprocinfo; FmgrInfo *normprocinfo;
FmgrInfo *normalizeprocinfo;
Oid collation; Oid collation;
/* Lists */ /* Lists */
@@ -273,7 +278,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 rel, uint16 procnum); FmgrInfo *IvfflatOptionalProcInfo(Relation rel, uint16 procnum);
bool IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result); void IvfflatNormValue(FmgrInfo *procinfo, FmgrInfo *normalizeprocinfo, Oid collation, Datum *value, Vector * result);
int IvfflatGetLists(Relation index); int IvfflatGetLists(Relation index);
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);
void IvfflatCommitBuffer(Buffer buf, GenericXLogState *state); void IvfflatCommitBuffer(Buffer buf, GenericXLogState *state);

View File

@@ -68,6 +68,7 @@ InsertTuple(Relation rel, Datum *values, bool *isnull, ItemPointer heap_tid, Rel
IndexTuple itup; IndexTuple itup;
Datum value; Datum value;
FmgrInfo *normprocinfo; FmgrInfo *normprocinfo;
FmgrInfo *normalizeprocinfo;
Buffer buf; Buffer buf;
Page page; Page page;
GenericXLogState *state; GenericXLogState *state;
@@ -81,11 +82,8 @@ InsertTuple(Relation rel, Datum *values, bool *isnull, ItemPointer heap_tid, Rel
/* Normalize if needed */ /* Normalize if needed */
normprocinfo = IvfflatOptionalProcInfo(rel, IVFFLAT_NORM_PROC); normprocinfo = IvfflatOptionalProcInfo(rel, IVFFLAT_NORM_PROC);
if (normprocinfo != NULL) normalizeprocinfo = IvfflatOptionalProcInfo(rel, IVFFLAT_NORMALIZE_PROC);
{ IvfflatNormValue(normprocinfo, normalizeprocinfo, rel->rd_indcollation[0], &value, NULL);
if (!IvfflatNormValue(normprocinfo, rel->rd_indcollation[0], &value, NULL))
return;
}
/* Find the insert page - sets the page and list info */ /* Find the insert page - sets the page and list info */
FindInsertPage(rel, values, &insertPage, &listInfo); FindInsertPage(rel, values, &insertPage, &listInfo);

View File

@@ -232,6 +232,7 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
/* Set support functions */ /* Set support functions */
so->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC); so->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC);
so->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC); so->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
so->normalizeprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC);
so->collation = index->rd_indcollation[0]; so->collation = index->rd_indcollation[0];
/* Create tuple description for sorting */ /* Create tuple description for sorting */
@@ -319,8 +320,7 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value))); Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
/* Fine if normalization fails */ /* Fine if normalization fails */
if (so->normprocinfo != NULL) IvfflatNormValue(so->normprocinfo, so->normalizeprocinfo, so->collation, &value, NULL);
IvfflatNormValue(so->normprocinfo, so->collation, &value, NULL);
} }
IvfflatBench("GetScanLists", GetScanLists(scan, value)); IvfflatBench("GetScanLists", GetScanLists(scan, value));

View File

@@ -66,17 +66,26 @@ IvfflatOptionalProcInfo(Relation rel, uint16 procnum)
} }
/* /*
* Divide by the norm * Normalize a vector
*
* Returns false if value should not be indexed
* *
* The caller needs to free the pointer stored in value * The caller needs to free the pointer stored in value
* if it's different than the original value * if it's different than the original value
*/ */
bool void
IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result) IvfflatNormValue(FmgrInfo *procinfo, FmgrInfo *normalizeprocinfo, Oid collation, Datum *value, Vector * result)
{ {
double norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value)); double norm;
if (normalizeprocinfo != NULL)
{
*value = FunctionCall1Coll(normalizeprocinfo, collation, *value);
return;
}
if (procinfo == NULL)
return;
norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value));
if (norm > 0) if (norm > 0)
{ {
@@ -89,11 +98,7 @@ IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * resul
result->x[i] = v->x[i] / norm; result->x[i] = v->x[i] / norm;
*value = PointerGetDatum(result); *value = PointerGetDatum(result);
return true;
} }
return false;
} }
/* /*

View File

@@ -745,6 +745,52 @@ vector_norm(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt((double) norm)); PG_RETURN_FLOAT8(sqrt((double) norm));
} }
/*
* Normalize a vector with the L2 norm
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(normalize_l2);
Datum
normalize_l2(PG_FUNCTION_ARGS)
{
Vector *a = PG_GETARG_VECTOR_P(0);
float *ax = a->x;
double norm = 0.0;
#ifdef _MSC_VER
/* Fix precision on Windows */
double normf;
#else
float normf;
#endif
Vector *result;
float *rx;
result = InitVector(a->dim);
rx = result->x;
/* Auto-vectorized */
for (int i = 0; i < a->dim; i++)
norm += (double) ax[i] * (double) ax[i];
norm = sqrt(norm);
normf = norm;
if (normf > 0)
{
/* Auto-vectorized */
for (int i = 0, imax = a->dim; i < imax; i++)
rx[i] = ax[i] / normf;
/* Check for overflow */
for (int i = 0, imax = a->dim; i < imax; i++)
{
if (isinf(rx[i]))
float_overflow_error();
}
}
PG_RETURN_POINTER(result);
}
/* /*
* Add vectors * Add vectors
*/ */

View File

@@ -48,6 +48,36 @@ SELECT vector_norm('[0,1]');
1 1
(1 row) (1 row)
SELECT normalize_l2('[3,4]');
normalize_l2
--------------
[0.6,0.8]
(1 row)
SELECT normalize_l2('[3,0]');
normalize_l2
--------------
[1,0]
(1 row)
SELECT normalize_l2('[0,0.1]');
normalize_l2
--------------
[0,1]
(1 row)
SELECT normalize_l2('[0,0]');
normalize_l2
--------------
[0,0]
(1 row)
SELECT normalize_l2('[3e38]');
normalize_l2
--------------
[1]
(1 row)
SELECT l2_distance('[0,0]', '[3,4]'); SELECT l2_distance('[0,0]', '[3,4]');
l2_distance l2_distance
------------- -------------

View File

@@ -9,18 +9,19 @@ SELECT * FROM t ORDER BY val <=> '[3,3,3]';
[1,1,1] [1,1,1]
[1,2,3] [1,2,3]
[1,2,4] [1,2,4]
(3 rows) [0,0,0]
(4 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
count count
------- -------
3 4
(1 row) (1 row)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
count count
------- -------
3 4
(1 row) (1 row)
DROP TABLE t; DROP TABLE t;

View File

@@ -9,18 +9,19 @@ SELECT * FROM t ORDER BY val <=> '[3,3,3]';
[1,1,1] [1,1,1]
[1,2,3] [1,2,3]
[1,2,4] [1,2,4]
(3 rows) [0,0,0]
(4 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
count count
------- -------
3 4
(1 row) (1 row)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
count count
------- -------
3 4
(1 row) (1 row)
DROP TABLE t; DROP TABLE t;

View File

@@ -12,6 +12,12 @@ SELECT round(vector_norm('[1,1]')::numeric, 5);
SELECT vector_norm('[3,4]'); SELECT vector_norm('[3,4]');
SELECT vector_norm('[0,1]'); SELECT vector_norm('[0,1]');
SELECT normalize_l2('[3,4]');
SELECT normalize_l2('[3,0]');
SELECT normalize_l2('[0,0.1]');
SELECT normalize_l2('[0,0]');
SELECT normalize_l2('[3e38]');
SELECT l2_distance('[0,0]', '[3,4]'); SELECT l2_distance('[0,0]', '[3,4]');
SELECT l2_distance('[0,0]', '[0,1]'); SELECT l2_distance('[0,0]', '[0,1]');
SELECT l2_distance('[1,2]', '[3]'); SELECT l2_distance('[1,2]', '[3]');