mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 12:07:34 +08:00
Compare commits
1 Commits
half
...
half-index
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47852e8d15 |
@@ -46,45 +46,21 @@ CREATE FUNCTION half_negative_inner_product(half[], half[]) RETURNS float8
|
|||||||
CREATE FUNCTION float4_to_half(real, integer, boolean) RETURNS half
|
CREATE FUNCTION float4_to_half(real, integer, boolean) RETURNS half
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION half_to_float4(half, integer, boolean) RETURNS real
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION float8_to_half(float8, integer, boolean) RETURNS half
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION half_to_float8(half, integer, boolean) RETURNS float8
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION integer_to_half(integer, integer, boolean) RETURNS half
|
CREATE FUNCTION integer_to_half(integer, integer, boolean) RETURNS half
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION numeric_to_half(numeric, integer, boolean) RETURNS half
|
CREATE FUNCTION numeric_to_half(numeric, integer, boolean) RETURNS half
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION half_to_numeric(half, integer, boolean) RETURNS numeric
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE CAST (real AS half)
|
CREATE CAST (real AS half)
|
||||||
WITH FUNCTION float4_to_half(real, integer, boolean) AS IMPLICIT;
|
WITH FUNCTION float4_to_half(real, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
CREATE CAST (half AS real)
|
|
||||||
WITH FUNCTION half_to_float4(half, integer, boolean) AS IMPLICIT;
|
|
||||||
|
|
||||||
CREATE CAST (float8 AS half)
|
|
||||||
WITH FUNCTION float8_to_half(float8, integer, boolean) AS IMPLICIT;
|
|
||||||
|
|
||||||
CREATE CAST (half AS float8)
|
|
||||||
WITH FUNCTION half_to_float8(half, integer, boolean) AS IMPLICIT;
|
|
||||||
|
|
||||||
CREATE CAST (integer AS half)
|
CREATE CAST (integer AS half)
|
||||||
WITH FUNCTION integer_to_half(integer, integer, boolean) AS IMPLICIT;
|
WITH FUNCTION integer_to_half(integer, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
CREATE CAST (numeric AS half)
|
CREATE CAST (numeric AS half)
|
||||||
WITH FUNCTION numeric_to_half(numeric, integer, boolean) AS IMPLICIT;
|
WITH FUNCTION numeric_to_half(numeric, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
CREATE CAST (half AS numeric)
|
|
||||||
WITH FUNCTION half_to_numeric(half, integer, boolean) AS IMPLICIT;
|
|
||||||
|
|
||||||
CREATE OPERATOR <-> (
|
CREATE OPERATOR <-> (
|
||||||
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = l2_distance,
|
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = l2_distance,
|
||||||
COMMUTATOR = '<->'
|
COMMUTATOR = '<->'
|
||||||
@@ -99,3 +75,8 @@ CREATE OPERATOR <=> (
|
|||||||
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = cosine_distance,
|
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = cosine_distance,
|
||||||
COMMUTATOR = '<=>'
|
COMMUTATOR = '<=>'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS half_l2_ops
|
||||||
|
FOR TYPE half[] USING hnsw AS
|
||||||
|
OPERATOR 1 <-> (half[], half[]) FOR ORDER BY float_ops,
|
||||||
|
FUNCTION 1 half_l2_squared_distance(half[], half[]);
|
||||||
|
|||||||
@@ -344,47 +344,23 @@ CREATE FUNCTION half_negative_inner_product(half[], half[]) RETURNS float8
|
|||||||
CREATE FUNCTION float4_to_half(real, integer, boolean) RETURNS half
|
CREATE FUNCTION float4_to_half(real, integer, boolean) RETURNS half
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION half_to_float4(half, integer, boolean) RETURNS real
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION float8_to_half(float8, integer, boolean) RETURNS half
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION half_to_float8(half, integer, boolean) RETURNS float8
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION integer_to_half(integer, integer, boolean) RETURNS half
|
CREATE FUNCTION integer_to_half(integer, integer, boolean) RETURNS half
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION numeric_to_half(numeric, integer, boolean) RETURNS half
|
CREATE FUNCTION numeric_to_half(numeric, integer, boolean) RETURNS half
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION half_to_numeric(half, integer, boolean) RETURNS numeric
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
-- half casts
|
-- half casts
|
||||||
|
|
||||||
CREATE CAST (real AS half)
|
CREATE CAST (real AS half)
|
||||||
WITH FUNCTION float4_to_half(real, integer, boolean) AS IMPLICIT;
|
WITH FUNCTION float4_to_half(real, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
CREATE CAST (half AS real)
|
|
||||||
WITH FUNCTION half_to_float4(half, integer, boolean) AS IMPLICIT;
|
|
||||||
|
|
||||||
CREATE CAST (float8 AS half)
|
|
||||||
WITH FUNCTION float8_to_half(float8, integer, boolean) AS IMPLICIT;
|
|
||||||
|
|
||||||
CREATE CAST (half AS float8)
|
|
||||||
WITH FUNCTION half_to_float8(half, integer, boolean) AS IMPLICIT;
|
|
||||||
|
|
||||||
CREATE CAST (integer AS half)
|
CREATE CAST (integer AS half)
|
||||||
WITH FUNCTION integer_to_half(integer, integer, boolean) AS IMPLICIT;
|
WITH FUNCTION integer_to_half(integer, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
CREATE CAST (numeric AS half)
|
CREATE CAST (numeric AS half)
|
||||||
WITH FUNCTION numeric_to_half(numeric, integer, boolean) AS IMPLICIT;
|
WITH FUNCTION numeric_to_half(numeric, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
CREATE CAST (half AS numeric)
|
|
||||||
WITH FUNCTION half_to_numeric(half, integer, boolean) AS IMPLICIT;
|
|
||||||
|
|
||||||
-- half operators
|
-- half operators
|
||||||
|
|
||||||
CREATE OPERATOR <-> (
|
CREATE OPERATOR <-> (
|
||||||
@@ -401,3 +377,10 @@ CREATE OPERATOR <=> (
|
|||||||
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = cosine_distance,
|
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = cosine_distance,
|
||||||
COMMUTATOR = '<=>'
|
COMMUTATOR = '<=>'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- half opclasses
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS half_l2_ops
|
||||||
|
FOR TYPE half[] USING hnsw AS
|
||||||
|
OPERATOR 1 <-> (half[], half[]) FOR ORDER BY float_ops,
|
||||||
|
FUNCTION 1 half_l2_squared_distance(half[], half[]);
|
||||||
|
|||||||
109
src/half.c
109
src/half.c
@@ -103,7 +103,7 @@ DatumGetHalf(Datum X)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get a half from a message buffer
|
* Append a half to a StringInfo buffer
|
||||||
*/
|
*/
|
||||||
static half
|
static half
|
||||||
pq_getmsghalf(StringInfo msg)
|
pq_getmsghalf(StringInfo msg)
|
||||||
@@ -114,12 +114,13 @@ pq_getmsghalf(StringInfo msg)
|
|||||||
uint16 i;
|
uint16 i;
|
||||||
} swap;
|
} swap;
|
||||||
|
|
||||||
|
/* TODO Likely use float4 for clients */
|
||||||
swap.i = pq_getmsgint(msg, 2);
|
swap.i = pq_getmsgint(msg, 2);
|
||||||
return swap.h;
|
return swap.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Append a half to a StringInfo buffer
|
* Get a half from a message buffer
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
pq_sendhalf(StringInfo buf, half h)
|
pq_sendhalf(StringInfo buf, half h)
|
||||||
@@ -130,6 +131,7 @@ pq_sendhalf(StringInfo buf, half h)
|
|||||||
uint16 i;
|
uint16 i;
|
||||||
} swap;
|
} swap;
|
||||||
|
|
||||||
|
/* TODO Likely use float4 for clients */
|
||||||
swap.h = h;
|
swap.h = h;
|
||||||
pq_sendint16(buf, swap.i);
|
pq_sendint16(buf, swap.i);
|
||||||
}
|
}
|
||||||
@@ -279,32 +281,6 @@ Float4ToHalfUnchecked(float num)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if half is infinite
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
HalfIsInf(half num)
|
|
||||||
{
|
|
||||||
#ifdef FLT16_SUPPORT
|
|
||||||
return isinf(num);
|
|
||||||
#else
|
|
||||||
return (num << 1) == 0xF800;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if half is zero
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
HalfIsZero(half num)
|
|
||||||
{
|
|
||||||
#ifdef FLT16_SUPPORT
|
|
||||||
return num == 0;
|
|
||||||
#else
|
|
||||||
return (num << 1) == 0x0000;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert a float4 to a half
|
* Convert a float4 to a half
|
||||||
*/
|
*/
|
||||||
@@ -313,26 +289,10 @@ Float4ToHalf(float num)
|
|||||||
{
|
{
|
||||||
half result = Float4ToHalfUnchecked(num);
|
half result = Float4ToHalfUnchecked(num);
|
||||||
|
|
||||||
if (unlikely(HalfIsInf(result)) && !isinf(num))
|
/* TODO Perform checks without HalfToFloat4 */
|
||||||
|
if (unlikely(isinf(HalfToFloat4(result))) && !isinf(num))
|
||||||
float_overflow_error();
|
float_overflow_error();
|
||||||
if (unlikely(HalfIsZero(result)) && num != 0.0)
|
if (unlikely(HalfToFloat4(result) == 0.0f) && num != 0.0)
|
||||||
float_underflow_error();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert a float8 to a half
|
|
||||||
*/
|
|
||||||
static half
|
|
||||||
Float8ToHalf(double num)
|
|
||||||
{
|
|
||||||
/* TODO Convert directly for greater accuracy */
|
|
||||||
half result = Float4ToHalfUnchecked((float) num);
|
|
||||||
|
|
||||||
if (unlikely(HalfIsInf(result)) && !isinf(num))
|
|
||||||
float_overflow_error();
|
|
||||||
if (unlikely(HalfIsZero(result)) && num != 0.0)
|
|
||||||
float_underflow_error();
|
float_underflow_error();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -454,8 +414,6 @@ integer_to_half(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert numeric to half
|
* Convert numeric to half
|
||||||
*
|
|
||||||
* TODO Improve error message if out of range
|
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(numeric_to_half);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(numeric_to_half);
|
||||||
Datum
|
Datum
|
||||||
@@ -468,20 +426,6 @@ numeric_to_half(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_HALF(h);
|
PG_RETURN_HALF(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert half to numeric
|
|
||||||
*/
|
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_to_numeric);
|
|
||||||
Datum
|
|
||||||
half_to_numeric(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
half h = PG_GETARG_HALF(0);
|
|
||||||
float f = HalfToFloat4(h);
|
|
||||||
Numeric num = DatumGetNumeric(DirectFunctionCall1(float4_numeric, Float4GetDatum(f)));
|
|
||||||
|
|
||||||
PG_RETURN_NUMERIC(num);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert float4 to half
|
* Convert float4 to half
|
||||||
*/
|
*/
|
||||||
@@ -495,45 +439,6 @@ float4_to_half(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_HALF(h);
|
PG_RETURN_HALF(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert half to float4
|
|
||||||
*/
|
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_to_float4);
|
|
||||||
Datum
|
|
||||||
half_to_float4(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
half h = PG_GETARG_HALF(0);
|
|
||||||
float f = HalfToFloat4(h);
|
|
||||||
|
|
||||||
PG_RETURN_FLOAT4(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert float8 to half
|
|
||||||
*/
|
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(float8_to_half);
|
|
||||||
Datum
|
|
||||||
float8_to_half(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
float8 d = PG_GETARG_FLOAT8(0);
|
|
||||||
half h = Float8ToHalf(d);
|
|
||||||
|
|
||||||
PG_RETURN_HALF(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert half to float8
|
|
||||||
*/
|
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_to_float8);
|
|
||||||
Datum
|
|
||||||
half_to_float8(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
half h = PG_GETARG_HALF(0);
|
|
||||||
float f = HalfToFloat4(h);
|
|
||||||
|
|
||||||
PG_RETURN_FLOAT8((double) f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the L2 distance between half arrays
|
* Get the L2 distance between half arrays
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,7 +5,12 @@
|
|||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
#ifdef __FLT16_MAX__
|
/* _Float16 and __fp16 are not supported on x86_64 with GCC 11 */
|
||||||
|
#if defined(__is_identifier)
|
||||||
|
#if __is_identifier(_Float16)
|
||||||
|
#define FLT16_SUPPORT
|
||||||
|
#endif
|
||||||
|
#elif defined(FLT16_MAX)
|
||||||
#define FLT16_SUPPORT
|
#define FLT16_SUPPORT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -462,8 +462,8 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index
|
|||||||
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
|
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
|
||||||
|
|
||||||
/* 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 > HNSW_MAX_DIM)
|
if (buildstate->dimensions > HNSW_MAX_DIM)
|
||||||
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", HNSW_MAX_DIM);
|
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", HNSW_MAX_DIM);
|
||||||
|
|||||||
@@ -58,12 +58,6 @@ SELECT '{1,2,3}'::half[];
|
|||||||
{1,2,3}
|
{1,2,3}
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT '{1,2,3}'::half[]::real[];
|
|
||||||
float4
|
|
||||||
---------
|
|
||||||
{1,2,3}
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '65505'::integer::half;
|
SELECT '65505'::integer::half;
|
||||||
half
|
half
|
||||||
-------
|
-------
|
||||||
@@ -82,44 +76,6 @@ SELECT 'Infinity'::real::half;
|
|||||||
Infinity
|
Infinity
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT '1e-38'::real::half;
|
|
||||||
ERROR: value out of range: underflow
|
|
||||||
SELECT '1.5'::half::real;
|
|
||||||
float4
|
|
||||||
--------
|
|
||||||
1.5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '1.5'::real::half;
|
|
||||||
half
|
|
||||||
------
|
|
||||||
1.5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '1.5'::half::double precision;
|
|
||||||
float8
|
|
||||||
--------
|
|
||||||
1.5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '1.5'::double precision::half;
|
|
||||||
half
|
|
||||||
------
|
|
||||||
1.5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '1.5'::half::numeric;
|
|
||||||
numeric
|
|
||||||
---------
|
|
||||||
1.5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '1.5'::numeric::half;
|
|
||||||
half
|
|
||||||
------
|
|
||||||
1.5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_distance('{0,0}'::half[], '{3,4}'::half[]);
|
SELECT l2_distance('{0,0}'::half[], '{3,4}'::half[]);
|
||||||
l2_distance
|
l2_distance
|
||||||
-------------
|
-------------
|
||||||
|
|||||||
@@ -12,21 +12,10 @@ SELECT '1.5 '::half;
|
|||||||
SELECT '1.5a'::half;
|
SELECT '1.5a'::half;
|
||||||
|
|
||||||
SELECT '{1,2,3}'::half[];
|
SELECT '{1,2,3}'::half[];
|
||||||
SELECT '{1,2,3}'::half[]::real[];
|
|
||||||
|
|
||||||
SELECT '65505'::integer::half;
|
SELECT '65505'::integer::half;
|
||||||
SELECT 'NaN'::real::half;
|
SELECT 'NaN'::real::half;
|
||||||
SELECT 'Infinity'::real::half;
|
SELECT 'Infinity'::real::half;
|
||||||
SELECT '1e-38'::real::half;
|
|
||||||
|
|
||||||
SELECT '1.5'::half::real;
|
|
||||||
SELECT '1.5'::real::half;
|
|
||||||
|
|
||||||
SELECT '1.5'::half::double precision;
|
|
||||||
SELECT '1.5'::double precision::half;
|
|
||||||
|
|
||||||
SELECT '1.5'::half::numeric;
|
|
||||||
SELECT '1.5'::numeric::half;
|
|
||||||
|
|
||||||
SELECT l2_distance('{0,0}'::half[], '{3,4}'::half[]);
|
SELECT l2_distance('{0,0}'::half[], '{3,4}'::half[]);
|
||||||
SELECT l2_distance('{0,0}'::half[], '{0,1}'::half[]);
|
SELECT l2_distance('{0,0}'::half[], '{0,1}'::half[]);
|
||||||
|
|||||||
93
test/t/019_hnsw_half.pl
Normal file
93
test/t/019_hnsw_half.pl
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use PostgresNode;
|
||||||
|
use TestLib;
|
||||||
|
use Test::More;
|
||||||
|
|
||||||
|
my $node;
|
||||||
|
my @queries = ();
|
||||||
|
my @expected;
|
||||||
|
my $limit = 20;
|
||||||
|
|
||||||
|
sub test_recall
|
||||||
|
{
|
||||||
|
my ($min, $operator) = @_;
|
||||||
|
my $correct = 0;
|
||||||
|
my $total = 0;
|
||||||
|
|
||||||
|
my $explain = $node->safe_psql("postgres", qq(
|
||||||
|
SET enable_seqscan = off;
|
||||||
|
EXPLAIN ANALYZE SELECT i FROM tst ORDER BY v $operator '$queries[0]' LIMIT $limit;
|
||||||
|
));
|
||||||
|
like($explain, qr/Index Scan/);
|
||||||
|
|
||||||
|
for my $i (0 .. $#queries)
|
||||||
|
{
|
||||||
|
my $actual = $node->safe_psql("postgres", qq(
|
||||||
|
SET enable_seqscan = off;
|
||||||
|
SELECT i FROM tst ORDER BY v $operator '$queries[$i]' LIMIT $limit;
|
||||||
|
));
|
||||||
|
my @actual_ids = split("\n", $actual);
|
||||||
|
my %actual_set = map { $_ => 1 } @actual_ids;
|
||||||
|
|
||||||
|
my @expected_ids = split("\n", $expected[$i]);
|
||||||
|
|
||||||
|
foreach (@expected_ids)
|
||||||
|
{
|
||||||
|
if (exists($actual_set{$_}))
|
||||||
|
{
|
||||||
|
$correct++;
|
||||||
|
}
|
||||||
|
$total++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmp_ok($correct / $total, ">=", $min, $operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Initialize node
|
||||||
|
$node = get_new_node('node');
|
||||||
|
$node->init;
|
||||||
|
$node->start;
|
||||||
|
|
||||||
|
# Create table
|
||||||
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
|
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v half[3]);");
|
||||||
|
$node->safe_psql("postgres",
|
||||||
|
"INSERT INTO tst SELECT i, ARRAY[random(), random(), random()]::numeric[]::half[] FROM generate_series(1, 10000) i;"
|
||||||
|
);
|
||||||
|
|
||||||
|
# Generate queries
|
||||||
|
for (1 .. 20)
|
||||||
|
{
|
||||||
|
my $r1 = rand();
|
||||||
|
my $r2 = rand();
|
||||||
|
my $r3 = rand();
|
||||||
|
push(@queries, "{$r1,$r2,$r3}");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check each index type
|
||||||
|
my @operators = ("<->");
|
||||||
|
my @opclasses = ("half_l2_ops");
|
||||||
|
|
||||||
|
for my $i (0 .. $#operators)
|
||||||
|
{
|
||||||
|
my $operator = $operators[$i];
|
||||||
|
my $opclass = $opclasses[$i];
|
||||||
|
|
||||||
|
# Get exact results
|
||||||
|
@expected = ();
|
||||||
|
foreach (@queries)
|
||||||
|
{
|
||||||
|
my $res = $node->safe_psql("postgres", "SELECT i FROM tst ORDER BY v $operator '$_' LIMIT $limit;");
|
||||||
|
push(@expected, $res);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add index
|
||||||
|
$node->safe_psql("postgres", "CREATE INDEX ON tst USING hnsw (v $opclass);");
|
||||||
|
|
||||||
|
my $min = $operator eq "<#>" ? 0.80 : 0.99;
|
||||||
|
test_recall($min, $operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
done_testing();
|
||||||
Reference in New Issue
Block a user