pgvector.git / summary / log / commit / refs
commit c68c2867fdf4875c6b052ca986162de22cf782bc
Author: Andrew Kane <andrew@ankane.org>
Date: Sun Apr 14 20:12:08 2024 +0000
Added more functions for halfvec
README.md | 5 +
sql/vector--0.6.2--0.7.0.sql | 85 ++++++++++++-
sql/vector.sql | 85 ++++++++++++-
src/halfutils.h | 13 ++
src/halfvec.c | 241 ++++++++++++++++++++++++++++++++++++
test/expected/halfvec_functions.out | 146 ++++++++++++++++++++++
test/sql/halfvec_functions.sql | 31 +++++
7 files changed, 604 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 42cba821..c4ce67e6 100644
--- a/README.md
+++ b/README.md
@@ -882,6 +882,9 @@ Each half vector takes `2 * dimensions + 8` bytes of storage. Each element is a
Operator | Description | Added
--- | --- | ---
+\+ | element-wise addition | unreleased
+\- | element-wise subtraction | unreleased
+\* | element-wise multiplication | unreleased
<-> | Euclidean distance | unreleased
<#> | negative inner product | unreleased
<=> | cosine distance | unreleased
@@ -891,6 +894,8 @@ Operator | Description | Added
Function | Description | Added
--- | --- | ---
cosine_distance(halfvec, halfvec) → double precision | cosine distance | unreleased
+halfvec_dims(halfvec) → integer | number of dimensions | unreleased
+halfvec_norm(halfvec) → double precision | Euclidean norm | unreleased
inner_product(halfvec, halfvec) → double precision | inner product | unreleased
l2_distance(halfvec, halfvec) → double precision | Euclidean distance | unreleased
l1_distance(halfvec, halfvec) → double precision | taxicab distance | unreleased
diff --git a/sql/vector--0.6.2--0.7.0.sql b/sql/vector--0.6.2--0.7.0.sql
index cc641ed5..d5b4e6b2 100644
--- a/sql/vector--0.6.2--0.7.0.sql
+++ b/sql/vector--0.6.2--0.7.0.sql
@@ -71,15 +71,48 @@ CREATE FUNCTION cosine_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;
+CREATE FUNCTION halfvec_dims(halfvec) RETURNS integer
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
CREATE FUNCTION halfvec_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+CREATE FUNCTION halfvec_add(halfvec, halfvec) RETURNS halfvec
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_sub(halfvec, halfvec) RETURNS halfvec
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_mul(halfvec, halfvec) RETURNS halfvec
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
CREATE FUNCTION quantize_binary(halfvec) RETURNS bit
AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION subvector(halfvec, int, int) RETURNS halfvec
AS 'MODULE_PATHNAME', 'halfvec_subvector' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+CREATE FUNCTION halfvec_lt(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_le(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_eq(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_ne(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_ge(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_gt(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_cmp(halfvec, halfvec) RETURNS int4
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
CREATE FUNCTION halfvec_l2_squared_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -140,6 +173,56 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>'
);
+CREATE OPERATOR + (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_add,
+ COMMUTATOR = +
+);
+
+CREATE OPERATOR - (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_sub
+);
+
+CREATE OPERATOR * (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_mul,
+ COMMUTATOR = *
+);
+
+CREATE OPERATOR < (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_lt,
+ COMMUTATOR = > , NEGATOR = >= ,
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR <= (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_le,
+ COMMUTATOR = >= , NEGATOR = > ,
+ RESTRICT = scalarlesel, JOIN = scalarlejoinsel
+);
+
+CREATE OPERATOR = (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_eq,
+ COMMUTATOR = = , NEGATOR = <> ,
+ RESTRICT = eqsel, JOIN = eqjoinsel
+);
+
+CREATE OPERATOR <> (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_ne,
+ COMMUTATOR = <> , NEGATOR = = ,
+ RESTRICT = eqsel, JOIN = eqjoinsel
+);
+
+CREATE OPERATOR >= (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_ge,
+ COMMUTATOR = <= , NEGATOR = < ,
+ RESTRICT = scalargesel, JOIN = scalargejoinsel
+);
+
+CREATE OPERATOR > (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_gt,
+ COMMUTATOR = < , NEGATOR = <= ,
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
CREATE OPERATOR CLASS halfvec_l2_ops
FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
@@ -184,7 +267,7 @@ CREATE FUNCTION vector_to_halfvec(vector, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (halfvec AS vector)
- WITH FUNCTION halfvec_to_vector(halfvec, integer, boolean) AS IMPLICIT;
+ WITH FUNCTION halfvec_to_vector(halfvec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (vector AS halfvec)
WITH FUNCTION vector_to_halfvec(vector, integer, boolean) AS IMPLICIT;
diff --git a/sql/vector.sql b/sql/vector.sql
index a0d59ddc..397c8392 100644
--- a/sql/vector.sql
+++ b/sql/vector.sql
@@ -364,9 +364,21 @@ CREATE FUNCTION cosine_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;
+CREATE FUNCTION halfvec_dims(halfvec) RETURNS integer
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
CREATE FUNCTION halfvec_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+CREATE FUNCTION halfvec_add(halfvec, halfvec) RETURNS halfvec
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_sub(halfvec, halfvec) RETURNS halfvec
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_mul(halfvec, halfvec) RETURNS halfvec
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
CREATE FUNCTION quantize_binary(halfvec) RETURNS bit
AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -375,6 +387,27 @@ CREATE FUNCTION subvector(halfvec, int, int) RETURNS halfvec
-- halfvec private functions
+CREATE FUNCTION halfvec_lt(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_le(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_eq(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_ne(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_ge(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_gt(halfvec, halfvec) RETURNS bool
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION halfvec_cmp(halfvec, halfvec) RETURNS int4
+ AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
CREATE FUNCTION halfvec_l2_squared_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -441,6 +474,56 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>'
);
+CREATE OPERATOR + (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_add,
+ COMMUTATOR = +
+);
+
+CREATE OPERATOR - (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_sub
+);
+
+CREATE OPERATOR * (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_mul,
+ COMMUTATOR = *
+);
+
+CREATE OPERATOR < (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_lt,
+ COMMUTATOR = > , NEGATOR = >= ,
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR <= (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_le,
+ COMMUTATOR = >= , NEGATOR = > ,
+ RESTRICT = scalarlesel, JOIN = scalarlejoinsel
+);
+
+CREATE OPERATOR = (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_eq,
+ COMMUTATOR = = , NEGATOR = <> ,
+ RESTRICT = eqsel, JOIN = eqjoinsel
+);
+
+CREATE OPERATOR <> (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_ne,
+ COMMUTATOR = <> , NEGATOR = = ,
+ RESTRICT = eqsel, JOIN = eqjoinsel
+);
+
+CREATE OPERATOR >= (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_ge,
+ COMMUTATOR = <= , NEGATOR = < ,
+ RESTRICT = scalargesel, JOIN = scalargejoinsel
+);
+
+CREATE OPERATOR > (
+ LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_gt,
+ COMMUTATOR = < , NEGATOR = <= ,
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
-- halfvec opclasses
CREATE OPERATOR CLASS halfvec_l2_ops
@@ -489,7 +572,7 @@ CREATE FUNCTION vector_to_halfvec(vector, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (halfvec AS vector)
- WITH FUNCTION halfvec_to_vector(halfvec, integer, boolean) AS IMPLICIT;
+ WITH FUNCTION halfvec_to_vector(halfvec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (vector AS halfvec)
WITH FUNCTION vector_to_halfvec(vector, integer, boolean) AS IMPLICIT;
diff --git a/src/halfutils.h b/src/halfutils.h
index 66ad23dc..58813155 100644
--- a/src/halfutils.h
+++ b/src/halfutils.h
@@ -41,6 +41,19 @@ HalfIsInf(half num)
#endif
}
+/*
+ * Check if half is zero
+ */
+static inline bool
+HalfIsZero(half num)
+{
+#ifdef FLT16_SUPPORT
+ return num == 0;
+#else
+ return (num & 0x7FFF) == 0x0000;
+#endif
+}
+
/*
* Convert a half to a float4
*/
diff --git a/src/halfvec.c b/src/halfvec.c
index cb4c8bfd..725960e1 100644
--- a/src/halfvec.c
+++ b/src/halfvec.c
@@ -146,6 +146,24 @@ halfvec_isspace(char ch)
return false;
}
+#if PG_VERSION_NUM < 120003
+static pg_noinline void
+float_overflow_error(void)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow")));
+}
+
+static pg_noinline void
+float_underflow_error(void)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow")));
+}
+#endif
+
/*
* Convert textual representation to internal representation
*/
@@ -677,6 +695,18 @@ halfvec_l1_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8((double) distance);
}
+/*
+ * Get the dimensions of a half vector
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_dims);
+Datum
+halfvec_dims(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+
+ PG_RETURN_INT32(a->dim);
+}
+
/*
* Get the L2 norm of a half vector
*/
@@ -699,6 +729,126 @@ halfvec_norm(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(norm));
}
+/*
+ * Add half vectors
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_add);
+Datum
+halfvec_add(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+ half *ax = a->x;
+ half *bx = b->x;
+ HalfVector *result;
+ half *rx;
+
+ CheckDims(a, b);
+
+ result = InitHalfVector(a->dim);
+ rx = result->x;
+
+ /* Auto-vectorized */
+ for (int i = 0, imax = a->dim; i < imax; i++)
+ {
+#ifdef FLT16_SUPPORT
+ rx[i] = ax[i] + bx[i];
+#else
+ rx[i] = Float4ToHalfUnchecked(HalfToFloat4(ax[i]) + HalfToFloat4(bx[i]));
+#endif
+ }
+
+ /* Check for overflow */
+ for (int i = 0, imax = a->dim; i < imax; i++)
+ {
+ if (HalfIsInf(rx[i]))
+ float_overflow_error();
+ }
+
+ PG_RETURN_POINTER(result);
+}
+
+/*
+ * Subtract half vectors
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_sub);
+Datum
+halfvec_sub(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+ half *ax = a->x;
+ half *bx = b->x;
+ HalfVector *result;
+ half *rx;
+
+ CheckDims(a, b);
+
+ result = InitHalfVector(a->dim);
+ rx = result->x;
+
+ /* Auto-vectorized */
+ for (int i = 0, imax = a->dim; i < imax; i++)
+ {
+#ifdef FLT16_SUPPORT
+ rx[i] = ax[i] - bx[i];
+#else
+ rx[i] = Float4ToHalfUnchecked(HalfToFloat4(ax[i]) - HalfToFloat4(bx[i]));
+#endif
+ }
+
+ /* Check for overflow */
+ for (int i = 0, imax = a->dim; i < imax; i++)
+ {
+ if (HalfIsInf(rx[i]))
+ float_overflow_error();
+ }
+
+ PG_RETURN_POINTER(result);
+}
+
+/*
+ * Multiply half vectors
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_mul);
+Datum
+halfvec_mul(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+ half *ax = a->x;
+ half *bx = b->x;
+ HalfVector *result;
+ half *rx;
+
+ CheckDims(a, b);
+
+ result = InitHalfVector(a->dim);
+ rx = result->x;
+
+ /* Auto-vectorized */
+ for (int i = 0, imax = a->dim; i < imax; i++)
+ {
+#ifdef FLT16_SUPPORT
+ rx[i] = ax[i] * bx[i];
+#else
+ rx[i] = Float4ToHalfUnchecked(HalfToFloat4(ax[i]) * HalfToFloat4(bx[i]));
+#endif
+ }
+
+ /* Check for overflow and underflow */
+ for (int i = 0, imax = a->dim; i < imax; i++)
+ {
+ if (HalfIsInf(rx[i]))
+ float_overflow_error();
+
+ if (HalfIsZero(rx[i]) && !(HalfIsZero(ax[i]) || HalfIsZero(bx[i])))
+ float_underflow_error();
+ }
+
+ PG_RETURN_POINTER(result);
+}
+
/*
* Quantize a half vector
*/
@@ -775,3 +925,94 @@ halfvec_cmp_internal(HalfVector * a, HalfVector * b)
return 0;
}
+
+/*
+ * Less than
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_lt);
+Datum
+halfvec_lt(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+
+ PG_RETURN_BOOL(halfvec_cmp_internal(a, b) < 0);
+}
+
+/*
+ * Less than or equal
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_le);
+Datum
+halfvec_le(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+
+ PG_RETURN_BOOL(halfvec_cmp_internal(a, b) <= 0);
+}
+
+/*
+ * Equal
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_eq);
+Datum
+halfvec_eq(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+
+ PG_RETURN_BOOL(halfvec_cmp_internal(a, b) == 0);
+}
+
+/*
+ * Not equal
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ne);
+Datum
+halfvec_ne(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+
+ PG_RETURN_BOOL(halfvec_cmp_internal(a, b) != 0);
+}
+
+/*
+ * Greater than or equal
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ge);
+Datum
+halfvec_ge(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+
+ PG_RETURN_BOOL(halfvec_cmp_internal(a, b) >= 0);
+}
+
+/*
+ * Greater than
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_gt);
+Datum
+halfvec_gt(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+
+ PG_RETURN_BOOL(halfvec_cmp_internal(a, b) > 0);
+}
+
+/*
+ * Compare half vectors
+ */
+PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_cmp);
+Datum
+halfvec_cmp(PG_FUNCTION_ARGS)
+{
+ HalfVector *a = PG_GETARG_HALFVEC_P(0);
+ HalfVector *b = PG_GETARG_HALFVEC_P(1);
+
+ PG_RETURN_INT32(halfvec_cmp_internal(a, b));
+}
diff --git a/test/expected/halfvec_functions.out b/test/expected/halfvec_functions.out
index 7bd76e51..70d904af 100644
--- a/test/expected/halfvec_functions.out
+++ b/test/expected/halfvec_functions.out
@@ -1,3 +1,149 @@
+SELECT '[1,2,3]'::halfvec + '[4,5,6]';
+ ?column?
+----------
+ [5,7,9]
+(1 row)
+
+SELECT '[65519]'::halfvec + '[65519]';
+ERROR: value out of range: overflow
+SELECT '[1,2,3]'::halfvec - '[4,5,6]';
+ ?column?
+------------
+ [-3,-3,-3]
+(1 row)
+
+SELECT '[-65519]'::halfvec - '[65519]';
+ERROR: value out of range: overflow
+SELECT '[1,2,3]'::halfvec * '[4,5,6]';
+ ?column?
+-----------
+ [4,10,18]
+(1 row)
+
+SELECT '[65519]'::halfvec * '[65519]';
+ERROR: value out of range: overflow
+SELECT '[1e-7]'::halfvec * '[1e-7]';
+ERROR: value out of range: underflow
+SELECT '[1,2,3]'::halfvec < '[1,2,3]';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '[1,2,3]'::halfvec < '[1,2]';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '[1,2,3]'::halfvec <= '[1,2,3]';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '[1,2,3]'::halfvec <= '[1,2]';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '[1,2,3]'::halfvec = '[1,2,3]';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '[1,2,3]'::halfvec = '[1,2]';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '[1,2,3]'::halfvec != '[1,2,3]';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '[1,2,3]'::halfvec != '[1,2]';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '[1,2,3]'::halfvec >= '[1,2,3]';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '[1,2,3]'::halfvec >= '[1,2]';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '[1,2,3]'::halfvec > '[1,2,3]';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '[1,2,3]'::halfvec > '[1,2]';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT halfvec_cmp('[1,2,3]', '[1,2,3]');
+ halfvec_cmp
+-------------
+ 0
+(1 row)
+
+SELECT halfvec_cmp('[1,2,3]', '[0,0,0]');
+ halfvec_cmp
+-------------
+ 1
+(1 row)
+
+SELECT halfvec_cmp('[0,0,0]', '[1,2,3]');
+ halfvec_cmp
+-------------
+ -1
+(1 row)
+
+SELECT halfvec_cmp('[1,2]', '[1,2,3]');
+ halfvec_cmp
+-------------
+ -1
+(1 row)
+
+SELECT halfvec_cmp('[1,2,3]', '[1,2]');
+ halfvec_cmp
+-------------
+ 1
+(1 row)
+
+SELECT halfvec_cmp('[1,2]', '[2,3,4]');
+ halfvec_cmp
+-------------
+ -1
+(1 row)
+
+SELECT halfvec_cmp('[2,3]', '[1,2,3]');
+ halfvec_cmp
+-------------
+ 1
+(1 row)
+
+SELECT halfvec_dims('[1,2,3]'::halfvec);
+ halfvec_dims
+--------------
+ 3
+(1 row)
+
SELECT round(halfvec_norm('[1,1]')::numeric, 5);
round
---------
diff --git a/test/sql/halfvec_functions.sql b/test/sql/halfvec_functions.sql
index d35bc87f..9f4943b6 100644
--- a/test/sql/halfvec_functions.sql
+++ b/test/sql/halfvec_functions.sql
@@ -1,3 +1,34 @@
+SELECT '[1,2,3]'::halfvec + '[4,5,6]';
+SELECT '[65519]'::halfvec + '[65519]';
+SELECT '[1,2,3]'::halfvec - '[4,5,6]';
+SELECT '[-65519]'::halfvec - '[65519]';
+SELECT '[1,2,3]'::halfvec * '[4,5,6]';
+SELECT '[65519]'::halfvec * '[65519]';
+SELECT '[1e-7]'::halfvec * '[1e-7]';
+
+SELECT '[1,2,3]'::halfvec < '[1,2,3]';
+SELECT '[1,2,3]'::halfvec < '[1,2]';
+SELECT '[1,2,3]'::halfvec <= '[1,2,3]';
+SELECT '[1,2,3]'::halfvec <= '[1,2]';
+SELECT '[1,2,3]'::halfvec = '[1,2,3]';
+SELECT '[1,2,3]'::halfvec = '[1,2]';
+SELECT '[1,2,3]'::halfvec != '[1,2,3]';
+SELECT '[1,2,3]'::halfvec != '[1,2]';
+SELECT '[1,2,3]'::halfvec >= '[1,2,3]';
+SELECT '[1,2,3]'::halfvec >= '[1,2]';
+SELECT '[1,2,3]'::halfvec > '[1,2,3]';
+SELECT '[1,2,3]'::halfvec > '[1,2]';
+
+SELECT halfvec_cmp('[1,2,3]', '[1,2,3]');
+SELECT halfvec_cmp('[1,2,3]', '[0,0,0]');
+SELECT halfvec_cmp('[0,0,0]', '[1,2,3]');
+SELECT halfvec_cmp('[1,2]', '[1,2,3]');
+SELECT halfvec_cmp('[1,2,3]', '[1,2]');
+SELECT halfvec_cmp('[1,2]', '[2,3,4]');
+SELECT halfvec_cmp('[2,3]', '[1,2,3]');
+
+SELECT halfvec_dims('[1,2,3]'::halfvec);
+
SELECT round(halfvec_norm('[1,1]')::numeric, 5);
SELECT halfvec_norm('[3,4]');
SELECT halfvec_norm('[0,1]');
[parent: 45cea3094345]