mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 20:15:46 +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
|
||||
matrix:
|
||||
include:
|
||||
- postgres: 17
|
||||
os: ubuntu-22.04
|
||||
- postgres: 16
|
||||
os: ubuntu-22.04
|
||||
- postgres: 15
|
||||
os: ubuntu-22.04
|
||||
- postgres: 14
|
||||
os: ubuntu-22.04
|
||||
- postgres: 13
|
||||
os: ubuntu-20.04
|
||||
- postgres: 12
|
||||
os: ubuntu-20.04
|
||||
steps:
|
||||
@@ -32,37 +24,6 @@ jobs:
|
||||
- run: |
|
||||
export PG_CONFIG=`which pg_config`
|
||||
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:
|
||||
runs-on: windows-latest
|
||||
if: ${{ !startsWith(github.ref_name, 'mac') }}
|
||||
@@ -95,20 +56,5 @@ jobs:
|
||||
git fetch origin ${{ github.ref }}
|
||||
git reset --hard FETCH_HEAD
|
||||
make
|
||||
make install
|
||||
chown -R postgres .
|
||||
sudo -u postgres make installcheck
|
||||
sudo -u postgres make prove_installcheck
|
||||
env:
|
||||
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);
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
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 */
|
||||
|
||||
/* Assumes same endianness for floats and integers */
|
||||
union
|
||||
{
|
||||
float f;
|
||||
uint32 i;
|
||||
} 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;
|
||||
/* TODO Use union to swap */
|
||||
uint16 bin = *((uint16 *) &num);
|
||||
uint32 exponent = (bin & 0x7C00) >> 10;
|
||||
uint32 mantissa = bin & 0x03FF;
|
||||
|
||||
/* Sign */
|
||||
result = (bin & 0x8000) << 16;
|
||||
uint32 result = (bin & 0x8000) << 16;
|
||||
|
||||
if (exponent == 31)
|
||||
{
|
||||
@@ -173,8 +156,8 @@ HalfToFloat4(half num)
|
||||
result |= mantissa << 13;
|
||||
}
|
||||
|
||||
swapfloat.i = result;
|
||||
return swapfloat.f;
|
||||
/* TODO Use union to swap */
|
||||
return *((float *) &result);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -190,30 +173,13 @@ Float4ToHalfUnchecked(float num)
|
||||
/* TODO Improve performance */
|
||||
|
||||
/* Assumes same endianness for floats and integers */
|
||||
union
|
||||
{
|
||||
float f;
|
||||
uint32 i;
|
||||
} 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;
|
||||
/* TODO Use union to swap */
|
||||
uint32 bin = *((uint32 *) &num);
|
||||
int exponent = (bin & 0x7F800000) >> 23;
|
||||
int mantissa = bin & 0x007FFFFF;
|
||||
|
||||
/* Sign */
|
||||
result = (bin & 0x80000000) >> 16;
|
||||
uint16 result = (bin & 0x80000000) >> 16;
|
||||
|
||||
if (isinf(num))
|
||||
{
|
||||
@@ -272,8 +238,8 @@ Float4ToHalfUnchecked(float num)
|
||||
}
|
||||
}
|
||||
|
||||
swaphalf.i = result;
|
||||
return swaphalf.h;
|
||||
/* TODO Use union to swap */
|
||||
return *((half *) & result);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -512,9 +478,6 @@ halfvec_in(PG_FUNCTION_ARGS)
|
||||
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
|
||||
*/
|
||||
@@ -526,6 +489,7 @@ halfvec_out(PG_FUNCTION_ARGS)
|
||||
int dim = vector->dim;
|
||||
char *buf;
|
||||
char *ptr;
|
||||
int n;
|
||||
|
||||
/*
|
||||
* Need:
|
||||
@@ -540,17 +504,21 @@ halfvec_out(PG_FUNCTION_ARGS)
|
||||
buf = (char *) palloc(FLOAT_SHORTEST_DECIMAL_LEN * dim + 2);
|
||||
ptr = buf;
|
||||
|
||||
AppendChar(ptr, '[');
|
||||
|
||||
*ptr = '[';
|
||||
ptr++;
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
AppendChar(ptr, ',');
|
||||
|
||||
AppendFloat(ptr, HalfToFloat4(vector->x[i]));
|
||||
{
|
||||
*ptr = ',';
|
||||
ptr++;
|
||||
}
|
||||
|
||||
AppendChar(ptr, ']');
|
||||
n = float_to_shortest_decimal_bufn(HalfToFloat4(vector->x[i]), ptr);
|
||||
ptr += n;
|
||||
}
|
||||
*ptr = ']';
|
||||
ptr++;
|
||||
*ptr = '\0';
|
||||
|
||||
PG_FREE_IF_COPY(vector, 0);
|
||||
@@ -763,18 +731,15 @@ Datum
|
||||
vector_to_halfvec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Vector *vec = PG_GETARG_VECTOR_P(0);
|
||||
int32 typmod = PG_GETARG_INT32(1);
|
||||
HalfVector *result;
|
||||
|
||||
CheckDim(vec->dim);
|
||||
CheckExpectedDim(typmod, vec->dim);
|
||||
|
||||
result = InitHalfVector(vec->dim);
|
||||
|
||||
for (int i = 0; i < vec->dim; i++)
|
||||
{
|
||||
result->x[i] = Float4ToHalfUnchecked(vec->x[i]);
|
||||
/* TODO Better error for overflow */
|
||||
CheckElement(result->x[i]);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#define HALF_MAX FLT16_MAX
|
||||
#else
|
||||
/* TODO #pragma message("")? */
|
||||
#define half uint16
|
||||
#define half __fp16
|
||||
#define HALF_MAX 65504
|
||||
#endif
|
||||
|
||||
|
||||
@@ -541,11 +541,9 @@ Datum
|
||||
halfvec_to_vector(PG_FUNCTION_ARGS)
|
||||
{
|
||||
HalfVector *vec = PG_GETARG_HALFVEC_P(0);
|
||||
int32 typmod = PG_GETARG_INT32(1);
|
||||
Vector *result;
|
||||
|
||||
CheckDim(vec->dim);
|
||||
CheckExpectedDim(typmod, vec->dim);
|
||||
|
||||
result = InitVector(vec->dim);
|
||||
|
||||
|
||||
@@ -58,12 +58,6 @@ SELECT '[1,2,3]'::halfvec::vector;
|
||||
[1,2,3]
|
||||
(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;
|
||||
halfvec
|
||||
---------
|
||||
|
||||
@@ -12,9 +12,6 @@ SELECT '{{1}}'::real[]::vector;
|
||||
SELECT '[1,2,3]'::vector::real[];
|
||||
SELECT '[1,2,3]'::vector::halfvec;
|
||||
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 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;
|
||||
|
||||
@@ -8,8 +8,6 @@ my $node;
|
||||
my @queries = ();
|
||||
my @expected;
|
||||
my $limit = 20;
|
||||
my $dim = 10;
|
||||
my $array_sql = join(",", ('random()') x $dim);
|
||||
|
||||
sub test_recall
|
||||
{
|
||||
@@ -54,20 +52,18 @@ $node->start;
|
||||
|
||||
# Create table
|
||||
$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",
|
||||
"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
|
||||
for (1 .. 20)
|
||||
{
|
||||
my @r = ();
|
||||
for (1 .. $dim)
|
||||
{
|
||||
push(@r, rand());
|
||||
}
|
||||
push(@queries, "[" . join(",", @r) . "]");
|
||||
my $r1 = rand();
|
||||
my $r2 = rand();
|
||||
my $r3 = rand();
|
||||
push(@queries, "[$r1,$r2,$r3]");
|
||||
}
|
||||
|
||||
# Check each index type
|
||||
@@ -94,7 +90,7 @@ for my $i (0 .. $#operators)
|
||||
));
|
||||
|
||||
# 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);
|
||||
|
||||
$node->safe_psql("postgres", "DROP INDEX idx;");
|
||||
|
||||
Reference in New Issue
Block a user