mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-23 20:40:55 +08:00
Compare commits
2 Commits
halfvec
...
halfvec-ci
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd8d04359a | ||
|
|
eeeacc771b |
54
.github/workflows/build.yml
vendored
54
.github/workflows/build.yml
vendored
@@ -8,16 +8,8 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- postgres: 17
|
|
||||||
os: ubuntu-22.04
|
|
||||||
- postgres: 16
|
- postgres: 16
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- postgres: 15
|
|
||||||
os: ubuntu-22.04
|
|
||||||
- postgres: 14
|
|
||||||
os: ubuntu-22.04
|
|
||||||
- postgres: 13
|
|
||||||
os: ubuntu-20.04
|
|
||||||
- postgres: 12
|
- postgres: 12
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
@@ -32,37 +24,6 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
export PG_CONFIG=`which pg_config`
|
export PG_CONFIG=`which pg_config`
|
||||||
sudo --preserve-env=PG_CONFIG make install
|
sudo --preserve-env=PG_CONFIG make install
|
||||||
- run: make installcheck
|
|
||||||
- if: ${{ failure() }}
|
|
||||||
run: cat regression.diffs
|
|
||||||
- run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install libipc-run-perl
|
|
||||||
- run: make prove_installcheck
|
|
||||||
mac:
|
|
||||||
runs-on: macos-latest
|
|
||||||
if: ${{ !startsWith(github.ref_name, 'windows') }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ankane/setup-postgres@v1
|
|
||||||
with:
|
|
||||||
postgres-version: 14
|
|
||||||
- run: make
|
|
||||||
env:
|
|
||||||
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter
|
|
||||||
- run: make install
|
|
||||||
- run: make installcheck
|
|
||||||
- if: ${{ failure() }}
|
|
||||||
run: cat regression.diffs
|
|
||||||
- run: |
|
|
||||||
brew install cpanm
|
|
||||||
cpanm --notest IPC::Run
|
|
||||||
wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_14_10.tar.gz
|
|
||||||
tar xf REL_14_10.tar.gz
|
|
||||||
- run: make prove_installcheck PROVE_FLAGS="-I ./postgres-REL_14_10/src/test/perl" PERL5LIB="/Users/runner/perl5/lib/perl5"
|
|
||||||
- run: make clean && /usr/local/opt/llvm@15/bin/scan-build --status-bugs make
|
|
||||||
env:
|
|
||||||
PG_CFLAGS: -DUSE_ASSERT_CHECKING
|
|
||||||
windows:
|
windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
if: ${{ !startsWith(github.ref_name, 'mac') }}
|
if: ${{ !startsWith(github.ref_name, 'mac') }}
|
||||||
@@ -95,20 +56,5 @@ jobs:
|
|||||||
git fetch origin ${{ github.ref }}
|
git fetch origin ${{ github.ref }}
|
||||||
git reset --hard FETCH_HEAD
|
git reset --hard FETCH_HEAD
|
||||||
make
|
make
|
||||||
make install
|
|
||||||
chown -R postgres .
|
|
||||||
sudo -u postgres make installcheck
|
|
||||||
sudo -u postgres make prove_installcheck
|
|
||||||
env:
|
env:
|
||||||
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
||||||
valgrind:
|
|
||||||
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ankane/setup-postgres-valgrind@v1
|
|
||||||
with:
|
|
||||||
postgres-version: 16
|
|
||||||
- run: make
|
|
||||||
- run: sudo --preserve-env=PG_CONFIG make install
|
|
||||||
- run: make installcheck
|
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -400,29 +400,6 @@ Use [partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html
|
|||||||
CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(category_id);
|
CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(category_id);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Types
|
|
||||||
|
|
||||||
- `vector`
|
|
||||||
- `halfvec`
|
|
||||||
|
|
||||||
## Half-Precision
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE items (id bigserial PRIMARY KEY, embedding halfvec(3));
|
|
||||||
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');
|
|
||||||
CREATE INDEX ON items USING hnsw ((embedding::halfvec(3)) halfvec_l2_ops);
|
|
||||||
```
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT id FROM items ORDER BY embedding::halfvec(3) <-> '[1,2,3]' LIMIT 5;
|
|
||||||
```
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT id FROM (
|
|
||||||
SELECT * FROM items ORDER BY embedding::halfvec(3) <-> '[1,2,3]' LIMIT 20
|
|
||||||
) ORDER BY embedding <-> '[1,2,3]' LIMIT 5;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hybrid Search
|
## Hybrid Search
|
||||||
|
|
||||||
Use together with Postgres [full-text search](https://www.postgresql.org/docs/current/textsearch-intro.html) for hybrid search.
|
Use together with Postgres [full-text search](https://www.postgresql.org/docs/current/textsearch-intro.html) for hybrid search.
|
||||||
|
|||||||
@@ -104,30 +104,13 @@ HalfToFloat4(half num)
|
|||||||
/* TODO Improve performance */
|
/* TODO Improve performance */
|
||||||
|
|
||||||
/* Assumes same endianness for floats and integers */
|
/* Assumes same endianness for floats and integers */
|
||||||
union
|
/* TODO Use union to swap */
|
||||||
{
|
uint16 bin = *((uint16 *) &num);
|
||||||
float f;
|
uint32 exponent = (bin & 0x7C00) >> 10;
|
||||||
uint32 i;
|
uint32 mantissa = bin & 0x03FF;
|
||||||
} swapfloat;
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
half h;
|
|
||||||
uint16 i;
|
|
||||||
} swaphalf;
|
|
||||||
|
|
||||||
uint16 bin;
|
|
||||||
uint32 exponent;
|
|
||||||
uint32 mantissa;
|
|
||||||
uint32 result;
|
|
||||||
|
|
||||||
swaphalf.h = num;
|
|
||||||
bin = swaphalf.i;
|
|
||||||
exponent = (bin & 0x7C00) >> 10;
|
|
||||||
mantissa = bin & 0x03FF;
|
|
||||||
|
|
||||||
/* Sign */
|
/* Sign */
|
||||||
result = (bin & 0x8000) << 16;
|
uint32 result = (bin & 0x8000) << 16;
|
||||||
|
|
||||||
if (exponent == 31)
|
if (exponent == 31)
|
||||||
{
|
{
|
||||||
@@ -173,8 +156,8 @@ HalfToFloat4(half num)
|
|||||||
result |= mantissa << 13;
|
result |= mantissa << 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
swapfloat.i = result;
|
/* TODO Use union to swap */
|
||||||
return swapfloat.f;
|
return *((float *) &result);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,30 +173,13 @@ Float4ToHalfUnchecked(float num)
|
|||||||
/* TODO Improve performance */
|
/* TODO Improve performance */
|
||||||
|
|
||||||
/* Assumes same endianness for floats and integers */
|
/* Assumes same endianness for floats and integers */
|
||||||
union
|
/* TODO Use union to swap */
|
||||||
{
|
uint32 bin = *((uint32 *) &num);
|
||||||
float f;
|
int exponent = (bin & 0x7F800000) >> 23;
|
||||||
uint32 i;
|
int mantissa = bin & 0x007FFFFF;
|
||||||
} swapfloat;
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
half h;
|
|
||||||
uint16 i;
|
|
||||||
} swaphalf;
|
|
||||||
|
|
||||||
uint32 bin;
|
|
||||||
int exponent;
|
|
||||||
int mantissa;
|
|
||||||
uint16 result;
|
|
||||||
|
|
||||||
swapfloat.f = num;
|
|
||||||
bin = swapfloat.i;
|
|
||||||
exponent = (bin & 0x7F800000) >> 23;
|
|
||||||
mantissa = bin & 0x007FFFFF;
|
|
||||||
|
|
||||||
/* Sign */
|
/* Sign */
|
||||||
result = (bin & 0x80000000) >> 16;
|
uint16 result = (bin & 0x80000000) >> 16;
|
||||||
|
|
||||||
if (isinf(num))
|
if (isinf(num))
|
||||||
{
|
{
|
||||||
@@ -272,8 +238,8 @@ Float4ToHalfUnchecked(float num)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
swaphalf.i = result;
|
/* TODO Use union to swap */
|
||||||
return swaphalf.h;
|
return *((half *) & result);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,9 +478,6 @@ halfvec_in(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_POINTER(result);
|
PG_RETURN_POINTER(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define AppendChar(ptr, c) (*(ptr)++ = (c))
|
|
||||||
#define AppendFloat(ptr, f) ((ptr) += float_to_shortest_decimal_bufn((f), (ptr)))
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert internal representation to textual representation
|
* Convert internal representation to textual representation
|
||||||
*/
|
*/
|
||||||
@@ -526,6 +489,7 @@ halfvec_out(PG_FUNCTION_ARGS)
|
|||||||
int dim = vector->dim;
|
int dim = vector->dim;
|
||||||
char *buf;
|
char *buf;
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
int n;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Need:
|
* Need:
|
||||||
@@ -540,17 +504,21 @@ halfvec_out(PG_FUNCTION_ARGS)
|
|||||||
buf = (char *) palloc(FLOAT_SHORTEST_DECIMAL_LEN * dim + 2);
|
buf = (char *) palloc(FLOAT_SHORTEST_DECIMAL_LEN * dim + 2);
|
||||||
ptr = buf;
|
ptr = buf;
|
||||||
|
|
||||||
AppendChar(ptr, '[');
|
*ptr = '[';
|
||||||
|
ptr++;
|
||||||
for (int i = 0; i < dim; i++)
|
for (int i = 0; i < dim; i++)
|
||||||
{
|
{
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
AppendChar(ptr, ',');
|
{
|
||||||
|
*ptr = ',';
|
||||||
AppendFloat(ptr, HalfToFloat4(vector->x[i]));
|
ptr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendChar(ptr, ']');
|
n = float_to_shortest_decimal_bufn(HalfToFloat4(vector->x[i]), ptr);
|
||||||
|
ptr += n;
|
||||||
|
}
|
||||||
|
*ptr = ']';
|
||||||
|
ptr++;
|
||||||
*ptr = '\0';
|
*ptr = '\0';
|
||||||
|
|
||||||
PG_FREE_IF_COPY(vector, 0);
|
PG_FREE_IF_COPY(vector, 0);
|
||||||
@@ -763,18 +731,15 @@ Datum
|
|||||||
vector_to_halfvec(PG_FUNCTION_ARGS)
|
vector_to_halfvec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *vec = PG_GETARG_VECTOR_P(0);
|
Vector *vec = PG_GETARG_VECTOR_P(0);
|
||||||
int32 typmod = PG_GETARG_INT32(1);
|
|
||||||
HalfVector *result;
|
HalfVector *result;
|
||||||
|
|
||||||
CheckDim(vec->dim);
|
CheckDim(vec->dim);
|
||||||
CheckExpectedDim(typmod, vec->dim);
|
|
||||||
|
|
||||||
result = InitHalfVector(vec->dim);
|
result = InitHalfVector(vec->dim);
|
||||||
|
|
||||||
for (int i = 0; i < vec->dim; i++)
|
for (int i = 0; i < vec->dim; i++)
|
||||||
{
|
{
|
||||||
result->x[i] = Float4ToHalfUnchecked(vec->x[i]);
|
result->x[i] = Float4ToHalfUnchecked(vec->x[i]);
|
||||||
/* TODO Better error for overflow */
|
|
||||||
CheckElement(result->x[i]);
|
CheckElement(result->x[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
#define HALF_MAX FLT16_MAX
|
#define HALF_MAX FLT16_MAX
|
||||||
#else
|
#else
|
||||||
/* TODO #pragma message("")? */
|
/* TODO #pragma message("")? */
|
||||||
#define half uint16
|
#define half __fp16
|
||||||
#define HALF_MAX 65504
|
#define HALF_MAX 65504
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -541,11 +541,9 @@ Datum
|
|||||||
halfvec_to_vector(PG_FUNCTION_ARGS)
|
halfvec_to_vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
HalfVector *vec = PG_GETARG_HALFVEC_P(0);
|
HalfVector *vec = PG_GETARG_HALFVEC_P(0);
|
||||||
int32 typmod = PG_GETARG_INT32(1);
|
|
||||||
Vector *result;
|
Vector *result;
|
||||||
|
|
||||||
CheckDim(vec->dim);
|
CheckDim(vec->dim);
|
||||||
CheckExpectedDim(typmod, vec->dim);
|
|
||||||
|
|
||||||
result = InitVector(vec->dim);
|
result = InitVector(vec->dim);
|
||||||
|
|
||||||
|
|||||||
@@ -58,12 +58,6 @@ SELECT '[1,2,3]'::halfvec::vector;
|
|||||||
[1,2,3]
|
[1,2,3]
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT '[1,2,3]'::vector::halfvec(2);
|
|
||||||
ERROR: expected 2 dimensions, not 3
|
|
||||||
SELECT '[1,2,3]'::halfvec::vector(2);
|
|
||||||
ERROR: expected 2 dimensions, not 3
|
|
||||||
SELECT '[65520]'::vector::halfvec;
|
|
||||||
ERROR: infinite value not allowed in halfvec
|
|
||||||
SELECT '[1e-8]'::vector::halfvec;
|
SELECT '[1e-8]'::vector::halfvec;
|
||||||
halfvec
|
halfvec
|
||||||
---------
|
---------
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ SELECT '{{1}}'::real[]::vector;
|
|||||||
SELECT '[1,2,3]'::vector::real[];
|
SELECT '[1,2,3]'::vector::real[];
|
||||||
SELECT '[1,2,3]'::vector::halfvec;
|
SELECT '[1,2,3]'::vector::halfvec;
|
||||||
SELECT '[1,2,3]'::halfvec::vector;
|
SELECT '[1,2,3]'::halfvec::vector;
|
||||||
SELECT '[1,2,3]'::vector::halfvec(2);
|
|
||||||
SELECT '[1,2,3]'::halfvec::vector(2);
|
|
||||||
SELECT '[65520]'::vector::halfvec;
|
|
||||||
SELECT '[1e-8]'::vector::halfvec;
|
SELECT '[1e-8]'::vector::halfvec;
|
||||||
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
|
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
|
||||||
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
|
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ my $node;
|
|||||||
my @queries = ();
|
my @queries = ();
|
||||||
my @expected;
|
my @expected;
|
||||||
my $limit = 20;
|
my $limit = 20;
|
||||||
my $dim = 10;
|
|
||||||
my $array_sql = join(",", ('random()') x $dim);
|
|
||||||
|
|
||||||
sub test_recall
|
sub test_recall
|
||||||
{
|
{
|
||||||
@@ -54,20 +52,18 @@ $node->start;
|
|||||||
|
|
||||||
# Create table
|
# Create table
|
||||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v halfvec($dim));");
|
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v halfvec(3));");
|
||||||
$node->safe_psql("postgres",
|
$node->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i, ARRAY[$array_sql] FROM generate_series(1, 10000) i;"
|
"INSERT INTO tst SELECT i, ARRAY[random(), random(), random()] FROM generate_series(1, 10000) i;"
|
||||||
);
|
);
|
||||||
|
|
||||||
# Generate queries
|
# Generate queries
|
||||||
for (1 .. 20)
|
for (1 .. 20)
|
||||||
{
|
{
|
||||||
my @r = ();
|
my $r1 = rand();
|
||||||
for (1 .. $dim)
|
my $r2 = rand();
|
||||||
{
|
my $r3 = rand();
|
||||||
push(@r, rand());
|
push(@queries, "[$r1,$r2,$r3]");
|
||||||
}
|
|
||||||
push(@queries, "[" . join(",", @r) . "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check each index type
|
# Check each index type
|
||||||
@@ -94,7 +90,7 @@ for my $i (0 .. $#operators)
|
|||||||
));
|
));
|
||||||
|
|
||||||
# Test approximate results
|
# Test approximate results
|
||||||
my $min = $operator eq "<#>" ? 0.95 : 0.99;
|
my $min = $operator eq "<#>" ? 0.80 : ($operator eq "<=>" ? 0.80 : 0.99);
|
||||||
test_recall($min, $operator);
|
test_recall($min, $operator);
|
||||||
|
|
||||||
$node->safe_psql("postgres", "DROP INDEX idx;");
|
$node->safe_psql("postgres", "DROP INDEX idx;");
|
||||||
|
|||||||
Reference in New Issue
Block a user