mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 03:57:34 +08:00
Compare commits
143 Commits
index-type
...
v0.7.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
103ac50f1a | ||
|
|
57fb706242 | ||
|
|
020d3edaa9 | ||
|
|
1e9e355175 | ||
|
|
f1d283f565 | ||
|
|
8684c2ba62 | ||
|
|
6c692ef23f | ||
|
|
bb424e96e7 | ||
|
|
30911edb7a | ||
|
|
5ae2bd9efb | ||
|
|
19215317a4 | ||
|
|
c7ca7f05de | ||
|
|
4733cf253b | ||
|
|
06d1fa1402 | ||
|
|
8c5a4bfb6c | ||
|
|
8772c8de68 | ||
|
|
d1694a93af | ||
|
|
61870a0244 | ||
|
|
9b89bed701 | ||
|
|
ad7cad5ecd | ||
|
|
2a8b9d689e | ||
|
|
18cd8a60c3 | ||
|
|
8c91a9f56a | ||
|
|
9249e7e2de | ||
|
|
9e91af5989 | ||
|
|
9dcf1bdc80 | ||
|
|
0eceaa3966 | ||
|
|
49c1f13095 | ||
|
|
ff9b22977e | ||
|
|
0468cbf6e6 | ||
|
|
258eaf58fd | ||
|
|
fa8d2df1cc | ||
|
|
69f49290fb | ||
|
|
ad91451266 | ||
|
|
cafd2f6641 | ||
|
|
7923c44efe | ||
|
|
9b269e2612 | ||
|
|
9894ca3e4e | ||
|
|
19cbbfdd69 | ||
|
|
24c8a2ff40 | ||
|
|
6df583a6f6 | ||
|
|
999a2e53dd | ||
|
|
3849f0fd3d | ||
|
|
df178472d1 | ||
|
|
a72511db7f | ||
|
|
b52beefbc6 | ||
|
|
1cc66543be | ||
|
|
b15930c9c0 | ||
|
|
6f2afb16ff | ||
|
|
0959e69529 | ||
|
|
1e94907179 | ||
|
|
c9fb66d54d | ||
|
|
48e68e5e42 | ||
|
|
78d32943ac | ||
|
|
ee29c51a49 | ||
|
|
cf494f15ac | ||
|
|
13bd194d00 | ||
|
|
0ddf65c2a3 | ||
|
|
1475c06902 | ||
|
|
7140a18283 | ||
|
|
7dcdaef96c | ||
|
|
abad3d81cc | ||
|
|
d516c9bd3f | ||
|
|
a172b7cafd | ||
|
|
3cbffb0e45 | ||
|
|
5e98f455e3 | ||
|
|
498a39d79b | ||
|
|
d9e22a31ca | ||
|
|
5ecf02f07b | ||
|
|
d188b56173 | ||
|
|
6247b302fc | ||
|
|
7f15221fb4 | ||
|
|
f23d7184e9 | ||
|
|
cd95d6dfa4 | ||
|
|
dc88135515 | ||
|
|
c91fc7e0f7 | ||
|
|
708da0e058 | ||
|
|
80d34830f6 | ||
|
|
68ac05e11e | ||
|
|
8daa581f42 | ||
|
|
ebc76114ad | ||
|
|
5dec500879 | ||
|
|
1fdfff7349 | ||
|
|
38e365ed58 | ||
|
|
58ec5296b0 | ||
|
|
47d5b2896e | ||
|
|
2bf1175ab0 | ||
|
|
ec640f3b57 | ||
|
|
91cf4d223e | ||
|
|
96fdf63787 | ||
|
|
914f9aa04a | ||
|
|
e9c3c42e1c | ||
|
|
c67dc6f9b0 | ||
|
|
c39cb25c32 | ||
|
|
8f00d679d6 | ||
|
|
52bfedddc2 | ||
|
|
0f4c2407dd | ||
|
|
3e924ab7ad | ||
|
|
cd8a25bc9a | ||
|
|
6bb5de3d1b | ||
|
|
15ee38456f | ||
|
|
25b98540c9 | ||
|
|
c4484c90d9 | ||
|
|
1129d23df9 | ||
|
|
cf46c3f651 | ||
|
|
fb6683ade7 | ||
|
|
558953ca6b | ||
|
|
b24ec26025 | ||
|
|
8b6eab25a3 | ||
|
|
69c3e719f7 | ||
|
|
e81547847f | ||
|
|
6ad81fc60d | ||
|
|
7ac0ac5a7f | ||
|
|
3eef1ff5c2 | ||
|
|
b8bdf317f0 | ||
|
|
78e5bcf229 | ||
|
|
4d21eea6f1 | ||
|
|
03ca9adc4c | ||
|
|
d244a040e1 | ||
|
|
c3448a25e2 | ||
|
|
053ce2ddae | ||
|
|
24c1b51099 | ||
|
|
9696835a19 | ||
|
|
b2a5259607 | ||
|
|
c198fd58ee | ||
|
|
8c408759dc | ||
|
|
14b351bc92 | ||
|
|
ad3f811fa3 | ||
|
|
281a74f54e | ||
|
|
034713c803 | ||
|
|
ed2e460f00 | ||
|
|
d136615874 | ||
|
|
d70b160e0a | ||
|
|
d1affcc667 | ||
|
|
158481ff2a | ||
|
|
794bbaecc7 | ||
|
|
8eddcfbd1d | ||
|
|
b609c343b4 | ||
|
|
bbfb3f200a | ||
|
|
99d367edc0 | ||
|
|
991743786a | ||
|
|
60ceaea4f2 | ||
|
|
9cd789fe06 |
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -9,9 +9,9 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- postgres: 17
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-24.04
|
||||
- postgres: 16
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-24.04
|
||||
- postgres: 15
|
||||
os: ubuntu-22.04
|
||||
- postgres: 14
|
||||
@@ -87,13 +87,14 @@ jobs:
|
||||
postgres-version: 14
|
||||
- run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && ^
|
||||
cd %TEMP% && ^
|
||||
nmake /NOLOGO /F Makefile.win && ^
|
||||
nmake /NOLOGO /F Makefile.win install && ^
|
||||
nmake /NOLOGO /F Makefile.win installcheck && ^
|
||||
nmake /NOLOGO /F Makefile.win clean && ^
|
||||
nmake /NOLOGO /F Makefile.win uninstall
|
||||
shell: cmd
|
||||
- if: ${{ failure() }}
|
||||
run: cat regression.diffs
|
||||
i386:
|
||||
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,4 +1,26 @@
|
||||
## 0.7.0 (unreleased)
|
||||
## 0.7.4 (2024-08-05)
|
||||
|
||||
- Fixed locking for parallel HNSW index builds
|
||||
- Fixed compilation error with GCC 14 on i386 when SSE2 is not enabled
|
||||
|
||||
## 0.7.3 (2024-07-22)
|
||||
|
||||
- Fixed `failed to add index item` error with `sparsevec`
|
||||
- Fixed compilation error with FreeBSD ARM
|
||||
- Fixed compilation warning with MSVC and Postgres 16
|
||||
|
||||
## 0.7.2 (2024-06-11)
|
||||
|
||||
- Fixed initialization fork for indexes on unlogged tables
|
||||
|
||||
## 0.7.1 (2024-06-03)
|
||||
|
||||
- Improved performance of on-disk HNSW index builds
|
||||
- Fixed `undefined symbol` error with GCC 8
|
||||
- Fixed compilation error with universal binaries on Mac
|
||||
- Fixed compilation warning with Clang < 14
|
||||
|
||||
## 0.7.0 (2024-04-29)
|
||||
|
||||
- Added `halfvec` type
|
||||
- Added `sparsevec` type
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "vector",
|
||||
"abstract": "Open-source vector similarity search for Postgres",
|
||||
"description": "Supports L2 distance, inner product, and cosine distance",
|
||||
"version": "0.6.2",
|
||||
"version": "0.7.4",
|
||||
"maintainer": [
|
||||
"Andrew Kane <andrew@ankane.org>"
|
||||
],
|
||||
@@ -20,7 +20,7 @@
|
||||
"vector": {
|
||||
"file": "sql/vector.sql",
|
||||
"docfile": "README.md",
|
||||
"version": "0.6.2",
|
||||
"version": "0.7.4",
|
||||
"abstract": "Open-source vector similarity search for Postgres"
|
||||
}
|
||||
},
|
||||
|
||||
10
Makefile
10
Makefile
@@ -1,8 +1,9 @@
|
||||
EXTENSION = vector
|
||||
EXTVERSION = 0.6.2
|
||||
EXTVERSION = 0.7.4
|
||||
|
||||
MODULE_big = vector
|
||||
DATA = $(wildcard sql/*--*.sql)
|
||||
DATA = $(wildcard sql/*--*--*.sql)
|
||||
DATA_built = sql/$(EXTENSION)--$(EXTVERSION).sql
|
||||
OBJS = src/bitutils.o src/bitvec.o src/halfutils.o src/halfvec.o src/hnsw.o src/hnswbuild.o src/hnswinsert.o src/hnswscan.o src/hnswutils.o src/hnswvacuum.o src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/ivfvacuum.o src/sparsevec.o src/vector.o
|
||||
HEADERS = src/halfvec.h src/sparsevec.h src/vector.h
|
||||
|
||||
@@ -10,6 +11,7 @@ TESTS = $(wildcard test/sql/*.sql)
|
||||
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
|
||||
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
|
||||
|
||||
# To compile for portability, run: make OPTFLAGS=""
|
||||
OPTFLAGS = -march=native
|
||||
|
||||
# Mac ARM doesn't always support -march=native
|
||||
@@ -41,8 +43,6 @@ all: sql/$(EXTENSION)--$(EXTVERSION).sql
|
||||
sql/$(EXTENSION)--$(EXTVERSION).sql: sql/$(EXTENSION).sql
|
||||
cp $< $@
|
||||
|
||||
EXTRA_CLEAN = sql/$(EXTENSION)--$(EXTVERSION).sql
|
||||
|
||||
PG_CONFIG ?= pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS)
|
||||
@@ -52,7 +52,7 @@ ifeq ($(PROVE),)
|
||||
PROVE = prove
|
||||
endif
|
||||
|
||||
# for Postgres 15
|
||||
# for Postgres < 15
|
||||
PROVE_FLAGS += -I ./test/perl
|
||||
|
||||
prove_installcheck:
|
||||
|
||||
19
Makefile.win
19
Makefile.win
@@ -1,10 +1,11 @@
|
||||
EXTENSION = vector
|
||||
EXTVERSION = 0.6.2
|
||||
EXTVERSION = 0.7.4
|
||||
|
||||
DATA_built = sql\$(EXTENSION)--$(EXTVERSION).sql
|
||||
OBJS = src\bitutils.obj src\bitvec.obj src\halfutils.obj src\halfvec.obj src\hnsw.obj src\hnswbuild.obj src\hnswinsert.obj src\hnswscan.obj src\hnswutils.obj src\hnswvacuum.obj src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\sparsevec.obj src\vector.obj
|
||||
HEADERS = src\halfvec.h src\sparsevec.h src\vector.h
|
||||
|
||||
REGRESS = bit_functions btree_halfvec btree_sparsevec btree_vector cast copy halfvec_functions halfvec_input hnsw_bit_hamming hnsw_bit_jaccard hnsw_halfvec_cosine hnsw_halfvec_ip hnsw_halfvec_l2 hnsw_options hnsw_sparsevec_cosine hnsw_sparsevec_ip hnsw_sparsevec_l2 hnsw_unlogged hnsw_vector_cosine hnsw_vector_ip hnsw_vector_l2 ivfflat_halfvec_cosine ivfflat_halfvec_ip ivfflat_halfvec_l2 ivfflat_options ivfflat_unlogged ivfflat_vector_cosine ivfflat_vector_ip ivfflat_vector_l2 sparsevec_functions sparsevec_input vector_functions vector_input
|
||||
REGRESS = bit btree cast copy halfvec hnsw_bit hnsw_halfvec hnsw_sparsevec hnsw_vector ivfflat_bit ivfflat_halfvec ivfflat_vector sparsevec vector_type
|
||||
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
|
||||
|
||||
# For /arch flags
|
||||
@@ -19,11 +20,6 @@ PG_CFLAGS = $(PG_CFLAGS) $(OPTFLAGS) /O2 /fp:fast
|
||||
# https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/vectorizer-and-parallelizer-messages
|
||||
# PG_CFLAGS = $(PG_CFLAGS) /Qvec-report:2
|
||||
|
||||
all: sql\$(EXTENSION)--$(EXTVERSION).sql
|
||||
|
||||
sql\$(EXTENSION)--$(EXTVERSION).sql: sql\$(EXTENSION).sql
|
||||
copy sql\$(EXTENSION).sql $@
|
||||
|
||||
# TODO use pg_config
|
||||
!ifndef PGROOT
|
||||
!error PGROOT is not set
|
||||
@@ -43,15 +39,18 @@ SHLIB = $(EXTENSION).dll
|
||||
|
||||
LIBS = "$(LIBDIR)\postgres.lib"
|
||||
|
||||
all: $(SHLIB) $(DATA_built)
|
||||
|
||||
.c.obj:
|
||||
$(CC) $(CFLAGS) /c $< /Fo$@
|
||||
|
||||
$(SHLIB): $(OBJS)
|
||||
$(CC) $(CFLAGS) $(OBJS) $(LIBS) /link /DLL /OUT:$(SHLIB)
|
||||
|
||||
all: $(SHLIB)
|
||||
sql\$(EXTENSION)--$(EXTVERSION).sql: sql\$(EXTENSION).sql
|
||||
copy sql\$(EXTENSION).sql $@
|
||||
|
||||
install:
|
||||
install: all
|
||||
copy $(SHLIB) "$(PKGLIBDIR)"
|
||||
copy $(EXTENSION).control "$(SHAREDIR)\extension"
|
||||
copy sql\$(EXTENSION)--*.sql "$(SHAREDIR)\extension"
|
||||
@@ -70,6 +69,6 @@ uninstall:
|
||||
|
||||
clean:
|
||||
del /f $(SHLIB) $(EXTENSION).lib $(EXTENSION).exp
|
||||
del /f $(DATA_built)
|
||||
del /f $(OBJS)
|
||||
del /f sql\$(EXTENSION)--$(EXTVERSION).sql
|
||||
del /f /s /q results regression.diffs regression.out tmp_check tmp_check_iso log output_iso
|
||||
|
||||
147
README.md
147
README.md
@@ -5,7 +5,8 @@ Open-source vector similarity search for Postgres
|
||||
Store your vectors with the rest of your data. Supports:
|
||||
|
||||
- exact and approximate nearest neighbor search
|
||||
- L2 distance, inner product, and cosine distance
|
||||
- single-precision, half-precision, binary, and sparse vectors
|
||||
- L2 distance, inner product, cosine distance, L1 distance, Hamming distance, and Jaccard distance
|
||||
- any [language](#languages) with a Postgres client
|
||||
|
||||
Plus [ACID](https://en.wikipedia.org/wiki/ACID) compliance, point-in-time recovery, JOINs, and all of the other [great features](https://www.postgresql.org/about/) of Postgres
|
||||
@@ -20,7 +21,7 @@ Compile and install the extension (supports Postgres 12+)
|
||||
|
||||
```sh
|
||||
cd /tmp
|
||||
git clone --branch v0.6.2 https://github.com/pgvector/pgvector.git
|
||||
git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git
|
||||
cd pgvector
|
||||
make
|
||||
make install # may need sudo
|
||||
@@ -45,7 +46,7 @@ Then use `nmake` to build:
|
||||
```cmd
|
||||
set "PGROOT=C:\Program Files\PostgreSQL\16"
|
||||
cd %TEMP%
|
||||
git clone --branch v0.6.2 https://github.com/pgvector/pgvector.git
|
||||
git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git
|
||||
cd pgvector
|
||||
nmake /F Makefile.win
|
||||
nmake /F Makefile.win install
|
||||
@@ -81,7 +82,7 @@ Get the nearest neighbors by L2 distance
|
||||
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
|
||||
```
|
||||
|
||||
Also supports inner product (`<#>`), cosine distance (`<=>`), and L1 distance (`<+>`, unreleased)
|
||||
Also supports inner product (`<#>`), cosine distance (`<=>`), and L1 distance (`<+>`, added in 0.7.0)
|
||||
|
||||
Note: `<#>` returns the negative inner product since Postgres only supports `ASC` order index scans on operators
|
||||
|
||||
@@ -143,7 +144,7 @@ Supported distance functions are:
|
||||
- `<->` - L2 distance
|
||||
- `<#>` - (negative) inner product
|
||||
- `<=>` - cosine distance
|
||||
- `<+>` - L1 distance (unreleased)
|
||||
- `<+>` - L1 distance (added in 0.7.0)
|
||||
|
||||
Get the nearest neighbors to a row
|
||||
|
||||
@@ -216,6 +217,8 @@ L2 distance
|
||||
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
|
||||
```
|
||||
|
||||
Note: Use `halfvec_l2_ops` for `halfvec` and `sparsevec_l2_ops` for `sparsevec` (and similar with the other distance functions)
|
||||
|
||||
Inner product
|
||||
|
||||
```sql
|
||||
@@ -228,19 +231,19 @@ Cosine distance
|
||||
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
|
||||
```
|
||||
|
||||
L1 distance - unreleased
|
||||
L1 distance - added in 0.7.0
|
||||
|
||||
```sql
|
||||
CREATE INDEX ON items USING hnsw (embedding vector_l1_ops);
|
||||
```
|
||||
|
||||
Hamming distance - unreleased
|
||||
Hamming distance - added in 0.7.0
|
||||
|
||||
```sql
|
||||
CREATE INDEX ON items USING hnsw (embedding bit_hamming_ops);
|
||||
```
|
||||
|
||||
Jaccard distance - unreleased
|
||||
Jaccard distance - added in 0.7.0
|
||||
|
||||
```sql
|
||||
CREATE INDEX ON items USING hnsw (embedding bit_jaccard_ops);
|
||||
@@ -249,9 +252,9 @@ CREATE INDEX ON items USING hnsw (embedding bit_jaccard_ops);
|
||||
Supported types are:
|
||||
|
||||
- `vector` - up to 2,000 dimensions
|
||||
- `halfvec` - up to 4,000 dimensions (unreleased)
|
||||
- `bit` - up to 64,000 dimensions (unreleased)
|
||||
- `sparsevec` - up to 1,000 non-zero elements (unreleased)
|
||||
- `halfvec` - up to 4,000 dimensions (added in 0.7.0)
|
||||
- `bit` - up to 64,000 dimensions (added in 0.7.0)
|
||||
- `sparsevec` - up to 1,000 non-zero elements (added in 0.7.0)
|
||||
|
||||
### Index Options
|
||||
|
||||
@@ -344,6 +347,8 @@ L2 distance
|
||||
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);
|
||||
```
|
||||
|
||||
Note: Use `halfvec_l2_ops` for `halfvec` (and similar with the other distance functions)
|
||||
|
||||
Inner product
|
||||
|
||||
```sql
|
||||
@@ -356,7 +361,7 @@ Cosine distance
|
||||
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
|
||||
```
|
||||
|
||||
Hamming distance - unreleased
|
||||
Hamming distance - added in 0.7.0
|
||||
|
||||
```sql
|
||||
CREATE INDEX ON items USING ivfflat (embedding bit_hamming_ops) WITH (lists = 100);
|
||||
@@ -365,8 +370,8 @@ CREATE INDEX ON items USING ivfflat (embedding bit_hamming_ops) WITH (lists = 10
|
||||
Supported types are:
|
||||
|
||||
- `vector` - up to 2,000 dimensions
|
||||
- `halfvec` - up to 4,000 dimensions (unreleased)
|
||||
- `bit` - up to 64,000 dimensions (unreleased)
|
||||
- `halfvec` - up to 4,000 dimensions (added in 0.7.0)
|
||||
- `bit` - up to 64,000 dimensions (added in 0.7.0)
|
||||
|
||||
### Query Options
|
||||
|
||||
@@ -440,9 +445,9 @@ 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);
|
||||
```
|
||||
|
||||
## Half Vectors
|
||||
## Half-Precision Vectors
|
||||
|
||||
*Unreleased*
|
||||
*Added in 0.7.0*
|
||||
|
||||
Use the `halfvec` type to store half-precision vectors
|
||||
|
||||
@@ -450,11 +455,11 @@ Use the `halfvec` type to store half-precision vectors
|
||||
CREATE TABLE items (id bigserial PRIMARY KEY, embedding halfvec(3));
|
||||
```
|
||||
|
||||
## Half Indexing
|
||||
## Half-Precision Indexing
|
||||
|
||||
*Unreleased*
|
||||
*Added in 0.7.0*
|
||||
|
||||
Index vectors at half precision for smaller indexes and faster build times
|
||||
Index vectors at half precision for smaller indexes
|
||||
|
||||
```sql
|
||||
CREATE INDEX ON items USING hnsw ((embedding::halfvec(3)) halfvec_l2_ops);
|
||||
@@ -475,23 +480,23 @@ CREATE TABLE items (id bigserial PRIMARY KEY, embedding bit(3));
|
||||
INSERT INTO items (embedding) VALUES ('000'), ('111');
|
||||
```
|
||||
|
||||
Get the nearest neighbors by Hamming distance
|
||||
|
||||
```sql
|
||||
SELECT * FROM items ORDER BY bit_count(embedding # '101') LIMIT 5;
|
||||
```
|
||||
|
||||
Or (unreleased)
|
||||
Get the nearest neighbors by Hamming distance (added in 0.7.0)
|
||||
|
||||
```sql
|
||||
SELECT * FROM items ORDER BY embedding <~> '101' LIMIT 5;
|
||||
```
|
||||
|
||||
Or (before 0.7.0)
|
||||
|
||||
```sql
|
||||
SELECT * FROM items ORDER BY bit_count(embedding # '101') LIMIT 5;
|
||||
```
|
||||
|
||||
Also supports Jaccard distance (`<%>`)
|
||||
|
||||
## Binary Quantization
|
||||
|
||||
*Unreleased*
|
||||
*Added in 0.7.0*
|
||||
|
||||
Use expression indexing for binary quantization
|
||||
|
||||
@@ -515,7 +520,7 @@ SELECT * FROM (
|
||||
|
||||
## Sparse Vectors
|
||||
|
||||
*Unreleased*
|
||||
*Added in 0.7.0*
|
||||
|
||||
Use the `sparsevec` type to store sparse vectors
|
||||
|
||||
@@ -550,7 +555,7 @@ You can use [Reciprocal Rank Fusion](https://github.com/pgvector/pgvector-python
|
||||
|
||||
## Indexing Subvectors
|
||||
|
||||
*Unreleased*
|
||||
*Added in 0.7.0*
|
||||
|
||||
Use expression indexing to index subvectors
|
||||
|
||||
@@ -726,7 +731,7 @@ Yes, pgvector uses the write-ahead log (WAL), which allows for replication and p
|
||||
|
||||
#### What if I want to index vectors with more than 2,000 dimensions?
|
||||
|
||||
You’ll need to use [dimensionality reduction](https://en.wikipedia.org/wiki/Dimensionality_reduction) at the moment.
|
||||
You can use [half-precision indexing](#half-precision-indexing) to index up to 4,000 dimensions or [binary quantization](#binary-quantization) to index up to 64,000 dimensions. Another option is [dimensionality reduction](https://en.wikipedia.org/wiki/Dimensionality_reduction).
|
||||
|
||||
#### Can I store vectors with different dimensions in the same column?
|
||||
|
||||
@@ -789,7 +794,7 @@ SELECT pg_size_pretty(pg_relation_size('index_name'));
|
||||
|
||||
#### Why isn’t a query using an index?
|
||||
|
||||
The query needs to have an `ORDER BY` and `LIMIT`, and the `ORDER BY` must be the result of a distance operator, not an expression.
|
||||
The query needs to have an `ORDER BY` and `LIMIT`, and the `ORDER BY` must be the result of a distance operator (not an expression) in ascending order.
|
||||
|
||||
```sql
|
||||
-- index
|
||||
@@ -864,23 +869,23 @@ Operator | Description | Added
|
||||
\+ | element-wise addition |
|
||||
\- | element-wise subtraction |
|
||||
\* | element-wise multiplication | 0.5.0
|
||||
\|\| | concatenate | unreleased
|
||||
\|\| | concatenate | 0.7.0
|
||||
<-> | Euclidean distance |
|
||||
<#> | negative inner product |
|
||||
<=> | cosine distance |
|
||||
<+> | taxicab distance | unreleased
|
||||
<+> | taxicab distance | 0.7.0
|
||||
|
||||
### Vector Functions
|
||||
|
||||
Function | Description | Added
|
||||
--- | --- | ---
|
||||
binary_quantize(vector) → bit | binary quantize | unreleased
|
||||
binary_quantize(vector) → bit | binary quantize | 0.7.0
|
||||
cosine_distance(vector, vector) → double precision | cosine distance |
|
||||
inner_product(vector, vector) → double precision | inner product |
|
||||
l1_distance(vector, vector) → double precision | taxicab distance | 0.5.0
|
||||
l2_distance(vector, vector) → double precision | Euclidean distance |
|
||||
l2_normalize(vector) → vector | Normalize with Euclidean norm | unreleased
|
||||
subvector(vector, integer, integer) → vector | subvector | unreleased
|
||||
l2_normalize(vector) → vector | Normalize with Euclidean norm | 0.7.0
|
||||
subvector(vector, integer, integer) → vector | subvector | 0.7.0
|
||||
vector_dims(vector) → integer | number of dimensions |
|
||||
vector_norm(vector) → double precision | Euclidean norm |
|
||||
|
||||
@@ -899,35 +904,35 @@ 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
|
||||
\|\| | concatenate | unreleased
|
||||
<-> | Euclidean distance | unreleased
|
||||
<#> | negative inner product | unreleased
|
||||
<=> | cosine distance | unreleased
|
||||
<+> | taxicab distance | unreleased
|
||||
\+ | element-wise addition | 0.7.0
|
||||
\- | element-wise subtraction | 0.7.0
|
||||
\* | element-wise multiplication | 0.7.0
|
||||
\|\| | concatenate | 0.7.0
|
||||
<-> | Euclidean distance | 0.7.0
|
||||
<#> | negative inner product | 0.7.0
|
||||
<=> | cosine distance | 0.7.0
|
||||
<+> | taxicab distance | 0.7.0
|
||||
|
||||
### Halfvec Functions
|
||||
|
||||
Function | Description | Added
|
||||
--- | --- | ---
|
||||
binary_quantize(halfvec) → bit | binary quantize | unreleased
|
||||
cosine_distance(halfvec, halfvec) → double precision | cosine distance | unreleased
|
||||
inner_product(halfvec, halfvec) → double precision | inner product | unreleased
|
||||
l1_distance(halfvec, halfvec) → double precision | taxicab distance | unreleased
|
||||
l2_distance(halfvec, halfvec) → double precision | Euclidean distance | unreleased
|
||||
l2_norm(halfvec) → double precision | Euclidean norm | unreleased
|
||||
l2_normalize(halfvec) → halfvec | Normalize with Euclidean norm | unreleased
|
||||
subvector(halfvec, integer, integer) → halfvec | subvector | unreleased
|
||||
vector_dims(halfvec) → integer | number of dimensions | unreleased
|
||||
binary_quantize(halfvec) → bit | binary quantize | 0.7.0
|
||||
cosine_distance(halfvec, halfvec) → double precision | cosine distance | 0.7.0
|
||||
inner_product(halfvec, halfvec) → double precision | inner product | 0.7.0
|
||||
l1_distance(halfvec, halfvec) → double precision | taxicab distance | 0.7.0
|
||||
l2_distance(halfvec, halfvec) → double precision | Euclidean distance | 0.7.0
|
||||
l2_norm(halfvec) → double precision | Euclidean norm | 0.7.0
|
||||
l2_normalize(halfvec) → halfvec | Normalize with Euclidean norm | 0.7.0
|
||||
subvector(halfvec, integer, integer) → halfvec | subvector | 0.7.0
|
||||
vector_dims(halfvec) → integer | number of dimensions | 0.7.0
|
||||
|
||||
### Halfvec Aggregate Functions
|
||||
|
||||
Function | Description | Added
|
||||
--- | --- | ---
|
||||
avg(halfvec) → halfvec | average | unreleased
|
||||
sum(halfvec) → halfvec | sum | unreleased
|
||||
avg(halfvec) → halfvec | average | 0.7.0
|
||||
sum(halfvec) → halfvec | sum | 0.7.0
|
||||
|
||||
### Bit Type
|
||||
|
||||
@@ -937,15 +942,15 @@ Each bit vector takes `dimensions / 8 + 8` bytes of storage. See the [Postgres d
|
||||
|
||||
Operator | Description | Added
|
||||
--- | --- | ---
|
||||
<~> | Hamming distance | unreleased
|
||||
<%> | Jaccard distance | unreleased
|
||||
<~> | Hamming distance | 0.7.0
|
||||
<%> | Jaccard distance | 0.7.0
|
||||
|
||||
### Bit Functions
|
||||
|
||||
Function | Description | Added
|
||||
--- | --- | ---
|
||||
hamming_distance(bit, bit) → double precision | Hamming distance | unreleased
|
||||
jaccard_distance(bit, bit) → double precision | Jaccard distance | unreleased
|
||||
hamming_distance(bit, bit) → double precision | Hamming distance | 0.7.0
|
||||
jaccard_distance(bit, bit) → double precision | Jaccard distance | 0.7.0
|
||||
|
||||
### Sparsevec Type
|
||||
|
||||
@@ -955,21 +960,21 @@ Each sparse vector takes `8 * non-zero elements + 16` bytes of storage. Each ele
|
||||
|
||||
Operator | Description | Added
|
||||
--- | --- | ---
|
||||
<-> | Euclidean distance | unreleased
|
||||
<#> | negative inner product | unreleased
|
||||
<=> | cosine distance | unreleased
|
||||
<+> | taxicab distance | unreleased
|
||||
<-> | Euclidean distance | 0.7.0
|
||||
<#> | negative inner product | 0.7.0
|
||||
<=> | cosine distance | 0.7.0
|
||||
<+> | taxicab distance | 0.7.0
|
||||
|
||||
### Sparsevec Functions
|
||||
|
||||
Function | Description | Added
|
||||
--- | --- | ---
|
||||
cosine_distance(sparsevec, sparsevec) → double precision | cosine distance | unreleased
|
||||
inner_product(sparsevec, sparsevec) → double precision | inner product | unreleased
|
||||
l1_distance(sparsevec, sparsevec) → double precision | taxicab distance | unreleased
|
||||
l2_distance(sparsevec, sparsevec) → double precision | Euclidean distance | unreleased
|
||||
l2_norm(sparsevec) → double precision | Euclidean norm | unreleased
|
||||
l2_normalize(sparsevec) → sparsevec | Normalize with Euclidean norm | unreleased
|
||||
cosine_distance(sparsevec, sparsevec) → double precision | cosine distance | 0.7.0
|
||||
inner_product(sparsevec, sparsevec) → double precision | inner product | 0.7.0
|
||||
l1_distance(sparsevec, sparsevec) → double precision | taxicab distance | 0.7.0
|
||||
l2_distance(sparsevec, sparsevec) → double precision | Euclidean distance | 0.7.0
|
||||
l2_norm(sparsevec) → double precision | Euclidean norm | 0.7.0
|
||||
l2_normalize(sparsevec) → sparsevec | Normalize with Euclidean norm | 0.7.0
|
||||
|
||||
## Installation Notes - Linux and Mac
|
||||
|
||||
@@ -1046,9 +1051,9 @@ This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (r
|
||||
You can also build the image manually:
|
||||
|
||||
```sh
|
||||
git clone --branch v0.6.2 https://github.com/pgvector/pgvector.git
|
||||
git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git
|
||||
cd pgvector
|
||||
docker build --build-arg PG_MAJOR=16 -t myuser/pgvector .
|
||||
docker build --pull --build-arg PG_MAJOR=16 -t myuser/pgvector .
|
||||
```
|
||||
|
||||
### Homebrew
|
||||
|
||||
@@ -22,52 +22,26 @@ CREATE OPERATOR || (
|
||||
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_concat
|
||||
);
|
||||
|
||||
CREATE FUNCTION ivfflat_halfvec_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
CREATE FUNCTION ivfflat_bit_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
CREATE FUNCTION hnsw_halfvec_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
CREATE FUNCTION hnsw_bit_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
CREATE FUNCTION hnsw_sparsevec_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
CREATE OPERATOR CLASS vector_l1_ops
|
||||
FOR TYPE vector USING hnsw AS
|
||||
OPERATOR 1 <+> (vector, vector) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 l1_distance(vector, vector);
|
||||
|
||||
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION bit_ivfflat_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION bit_hnsw_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE OPERATOR <~> (
|
||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance,
|
||||
COMMUTATOR = '<~>'
|
||||
);
|
||||
|
||||
CREATE OPERATOR <%> (
|
||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = jaccard_distance,
|
||||
COMMUTATOR = '<%>'
|
||||
);
|
||||
|
||||
CREATE OPERATOR CLASS bit_hamming_ops
|
||||
FOR TYPE bit USING ivfflat AS
|
||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 hamming_distance(bit, bit),
|
||||
FUNCTION 3 hamming_distance(bit, bit),
|
||||
FUNCTION 6 bit_ivfflat_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS bit_hamming_ops
|
||||
FOR TYPE bit USING hnsw AS
|
||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 hamming_distance(bit, bit),
|
||||
FUNCTION 4 bit_hnsw_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS bit_jaccard_ops
|
||||
FOR TYPE bit USING hnsw AS
|
||||
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 jaccard_distance(bit, bit),
|
||||
FUNCTION 4 bit_hnsw_support(internal);
|
||||
|
||||
CREATE TYPE halfvec;
|
||||
|
||||
CREATE FUNCTION halfvec_in(cstring, oid, integer) RETURNS halfvec
|
||||
@@ -169,17 +143,14 @@ CREATE FUNCTION halfvec_accum(double precision[], halfvec) RETURNS double precis
|
||||
CREATE FUNCTION halfvec_avg(double precision[]) RETURNS halfvec
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION halfvec_ivfflat_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION halfvec_hnsw_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
CREATE FUNCTION halfvec_combine(double precision[], double precision[]) RETURNS double precision[]
|
||||
AS 'MODULE_PATHNAME', 'vector_combine' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE AGGREGATE avg(halfvec) (
|
||||
SFUNC = halfvec_accum,
|
||||
STYPE = double precision[],
|
||||
FINALFUNC = halfvec_avg,
|
||||
COMBINEFUNC = vector_combine,
|
||||
COMBINEFUNC = halfvec_combine,
|
||||
INITCOND = '{0}',
|
||||
PARALLEL = SAFE
|
||||
);
|
||||
@@ -327,7 +298,7 @@ CREATE OPERATOR CLASS halfvec_l2_ops
|
||||
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
||||
FUNCTION 3 l2_distance(halfvec, halfvec),
|
||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
||||
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_ip_ops
|
||||
FOR TYPE halfvec USING ivfflat AS
|
||||
@@ -335,8 +306,7 @@ CREATE OPERATOR CLASS halfvec_ip_ops
|
||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
||||
FUNCTION 4 l2_norm(halfvec),
|
||||
FUNCTION 5 l2_normalize(halfvec),
|
||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
||||
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||
FOR TYPE halfvec USING ivfflat AS
|
||||
@@ -345,34 +315,67 @@ CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||
FUNCTION 2 l2_norm(halfvec),
|
||||
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
||||
FUNCTION 4 l2_norm(halfvec),
|
||||
FUNCTION 5 l2_normalize(halfvec),
|
||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
||||
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_l2_ops
|
||||
FOR TYPE halfvec USING hnsw AS
|
||||
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_ip_ops
|
||||
FOR TYPE halfvec USING hnsw AS
|
||||
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||
FOR TYPE halfvec USING hnsw AS
|
||||
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||
FUNCTION 2 l2_norm(halfvec),
|
||||
FUNCTION 3 l2_normalize(halfvec),
|
||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_l1_ops
|
||||
FOR TYPE halfvec USING hnsw AS
|
||||
OPERATOR 1 <+> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 l1_distance(halfvec, halfvec),
|
||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||
|
||||
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE OPERATOR <~> (
|
||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance,
|
||||
COMMUTATOR = '<~>'
|
||||
);
|
||||
|
||||
CREATE OPERATOR <%> (
|
||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = jaccard_distance,
|
||||
COMMUTATOR = '<%>'
|
||||
);
|
||||
|
||||
CREATE OPERATOR CLASS bit_hamming_ops
|
||||
FOR TYPE bit USING ivfflat AS
|
||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 hamming_distance(bit, bit),
|
||||
FUNCTION 3 hamming_distance(bit, bit),
|
||||
FUNCTION 5 ivfflat_bit_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS bit_hamming_ops
|
||||
FOR TYPE bit USING hnsw AS
|
||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 hamming_distance(bit, bit),
|
||||
FUNCTION 3 hnsw_bit_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS bit_jaccard_ops
|
||||
FOR TYPE bit USING hnsw AS
|
||||
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 jaccard_distance(bit, bit),
|
||||
FUNCTION 3 hnsw_bit_support(internal);
|
||||
|
||||
CREATE TYPE sparsevec;
|
||||
|
||||
@@ -460,9 +463,6 @@ CREATE FUNCTION halfvec_to_sparsevec(halfvec, integer, boolean) RETURNS sparseve
|
||||
CREATE FUNCTION sparsevec_to_halfvec(sparsevec, integer, boolean) RETURNS halfvec
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION sparsevec_hnsw_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE CAST (sparsevec AS sparsevec)
|
||||
WITH FUNCTION sparsevec(sparsevec, integer, boolean) AS IMPLICIT;
|
||||
|
||||
@@ -547,24 +547,23 @@ CREATE OPERATOR CLASS sparsevec_l2_ops
|
||||
FOR TYPE sparsevec USING hnsw AS
|
||||
OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 sparsevec_l2_squared_distance(sparsevec, sparsevec),
|
||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS sparsevec_ip_ops
|
||||
FOR TYPE sparsevec USING hnsw AS
|
||||
OPERATOR 1 <#> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS sparsevec_cosine_ops
|
||||
FOR TYPE sparsevec USING hnsw AS
|
||||
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
||||
FUNCTION 2 l2_norm(sparsevec),
|
||||
FUNCTION 3 l2_normalize(sparsevec),
|
||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS sparsevec_l1_ops
|
||||
FOR TYPE sparsevec USING hnsw AS
|
||||
OPERATOR 1 <+> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 l1_distance(sparsevec, sparsevec),
|
||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||
|
||||
2
sql/vector--0.7.0--0.7.1.sql
Normal file
2
sql/vector--0.7.0--0.7.1.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.1'" to load this file. \quit
|
||||
2
sql/vector--0.7.1--0.7.2.sql
Normal file
2
sql/vector--0.7.1--0.7.2.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.2'" to load this file. \quit
|
||||
2
sql/vector--0.7.2--0.7.3.sql
Normal file
2
sql/vector--0.7.2--0.7.3.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.3'" to load this file. \quit
|
||||
2
sql/vector--0.7.3--0.7.4.sql
Normal file
2
sql/vector--0.7.3--0.7.4.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.4'" to load this file. \quit
|
||||
145
sql/vector.sql
145
sql/vector.sql
@@ -261,6 +261,23 @@ CREATE ACCESS METHOD hnsw TYPE INDEX HANDLER hnswhandler;
|
||||
|
||||
COMMENT ON ACCESS METHOD hnsw IS 'hnsw index access method';
|
||||
|
||||
-- access method private functions
|
||||
|
||||
CREATE FUNCTION ivfflat_halfvec_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
CREATE FUNCTION ivfflat_bit_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
CREATE FUNCTION hnsw_halfvec_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
CREATE FUNCTION hnsw_bit_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
CREATE FUNCTION hnsw_sparsevec_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||
|
||||
-- vector opclasses
|
||||
|
||||
CREATE OPERATOR CLASS vector_ops
|
||||
@@ -314,55 +331,6 @@ CREATE OPERATOR CLASS vector_l1_ops
|
||||
OPERATOR 1 <+> (vector, vector) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 l1_distance(vector, vector);
|
||||
|
||||
-- bit functions
|
||||
|
||||
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
-- bit private functions
|
||||
|
||||
CREATE FUNCTION bit_ivfflat_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION bit_hnsw_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
-- bit operators
|
||||
|
||||
CREATE OPERATOR <~> (
|
||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance,
|
||||
COMMUTATOR = '<~>'
|
||||
);
|
||||
|
||||
CREATE OPERATOR <%> (
|
||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = jaccard_distance,
|
||||
COMMUTATOR = '<%>'
|
||||
);
|
||||
|
||||
-- bit opclasses
|
||||
|
||||
CREATE OPERATOR CLASS bit_hamming_ops
|
||||
FOR TYPE bit USING ivfflat AS
|
||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 hamming_distance(bit, bit),
|
||||
FUNCTION 3 hamming_distance(bit, bit),
|
||||
FUNCTION 6 bit_ivfflat_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS bit_hamming_ops
|
||||
FOR TYPE bit USING hnsw AS
|
||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 hamming_distance(bit, bit),
|
||||
FUNCTION 4 bit_hnsw_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS bit_jaccard_ops
|
||||
FOR TYPE bit USING hnsw AS
|
||||
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 jaccard_distance(bit, bit),
|
||||
FUNCTION 4 bit_hnsw_support(internal);
|
||||
|
||||
-- halfvec type
|
||||
|
||||
CREATE TYPE halfvec;
|
||||
@@ -470,11 +438,8 @@ CREATE FUNCTION halfvec_accum(double precision[], halfvec) RETURNS double precis
|
||||
CREATE FUNCTION halfvec_avg(double precision[]) RETURNS halfvec
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION halfvec_ivfflat_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION halfvec_hnsw_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
CREATE FUNCTION halfvec_combine(double precision[], double precision[]) RETURNS double precision[]
|
||||
AS 'MODULE_PATHNAME', 'vector_combine' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
-- halfvec aggregates
|
||||
|
||||
@@ -482,7 +447,7 @@ CREATE AGGREGATE avg(halfvec) (
|
||||
SFUNC = halfvec_accum,
|
||||
STYPE = double precision[],
|
||||
FINALFUNC = halfvec_avg,
|
||||
COMBINEFUNC = vector_combine,
|
||||
COMBINEFUNC = halfvec_combine,
|
||||
INITCOND = '{0}',
|
||||
PARALLEL = SAFE
|
||||
);
|
||||
@@ -638,7 +603,7 @@ CREATE OPERATOR CLASS halfvec_l2_ops
|
||||
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
||||
FUNCTION 3 l2_distance(halfvec, halfvec),
|
||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
||||
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_ip_ops
|
||||
FOR TYPE halfvec USING ivfflat AS
|
||||
@@ -646,8 +611,7 @@ CREATE OPERATOR CLASS halfvec_ip_ops
|
||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
||||
FUNCTION 4 l2_norm(halfvec),
|
||||
FUNCTION 5 l2_normalize(halfvec),
|
||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
||||
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||
FOR TYPE halfvec USING ivfflat AS
|
||||
@@ -656,34 +620,73 @@ CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||
FUNCTION 2 l2_norm(halfvec),
|
||||
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
||||
FUNCTION 4 l2_norm(halfvec),
|
||||
FUNCTION 5 l2_normalize(halfvec),
|
||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
||||
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_l2_ops
|
||||
FOR TYPE halfvec USING hnsw AS
|
||||
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_ip_ops
|
||||
FOR TYPE halfvec USING hnsw AS
|
||||
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||
FOR TYPE halfvec USING hnsw AS
|
||||
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||
FUNCTION 2 l2_norm(halfvec),
|
||||
FUNCTION 3 l2_normalize(halfvec),
|
||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS halfvec_l1_ops
|
||||
FOR TYPE halfvec USING hnsw AS
|
||||
OPERATOR 1 <+> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 l1_distance(halfvec, halfvec),
|
||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||
|
||||
-- bit functions
|
||||
|
||||
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
-- bit operators
|
||||
|
||||
CREATE OPERATOR <~> (
|
||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance,
|
||||
COMMUTATOR = '<~>'
|
||||
);
|
||||
|
||||
CREATE OPERATOR <%> (
|
||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = jaccard_distance,
|
||||
COMMUTATOR = '<%>'
|
||||
);
|
||||
|
||||
-- bit opclasses
|
||||
|
||||
CREATE OPERATOR CLASS bit_hamming_ops
|
||||
FOR TYPE bit USING ivfflat AS
|
||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 hamming_distance(bit, bit),
|
||||
FUNCTION 3 hamming_distance(bit, bit),
|
||||
FUNCTION 5 ivfflat_bit_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS bit_hamming_ops
|
||||
FOR TYPE bit USING hnsw AS
|
||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 hamming_distance(bit, bit),
|
||||
FUNCTION 3 hnsw_bit_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS bit_jaccard_ops
|
||||
FOR TYPE bit USING hnsw AS
|
||||
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 jaccard_distance(bit, bit),
|
||||
FUNCTION 3 hnsw_bit_support(internal);
|
||||
|
||||
--- sparsevec type
|
||||
|
||||
@@ -779,9 +782,6 @@ CREATE FUNCTION halfvec_to_sparsevec(halfvec, integer, boolean) RETURNS sparseve
|
||||
CREATE FUNCTION sparsevec_to_halfvec(sparsevec, integer, boolean) RETURNS halfvec
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION sparsevec_hnsw_support(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
-- sparsevec casts
|
||||
|
||||
CREATE CAST (sparsevec AS sparsevec)
|
||||
@@ -872,24 +872,23 @@ CREATE OPERATOR CLASS sparsevec_l2_ops
|
||||
FOR TYPE sparsevec USING hnsw AS
|
||||
OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 sparsevec_l2_squared_distance(sparsevec, sparsevec),
|
||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS sparsevec_ip_ops
|
||||
FOR TYPE sparsevec USING hnsw AS
|
||||
OPERATOR 1 <#> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS sparsevec_cosine_ops
|
||||
FOR TYPE sparsevec USING hnsw AS
|
||||
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
||||
FUNCTION 2 l2_norm(sparsevec),
|
||||
FUNCTION 3 l2_normalize(sparsevec),
|
||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||
|
||||
CREATE OPERATOR CLASS sparsevec_l1_ops
|
||||
FOR TYPE sparsevec USING hnsw AS
|
||||
OPERATOR 1 <+> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 l1_distance(sparsevec, sparsevec),
|
||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
||||
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "bitutils.h"
|
||||
#include "halfvec.h" /* for USE_DISPATCH and USE_TARGET_CLONES */
|
||||
#include "port/pg_bitutils.h"
|
||||
|
||||
#if defined(USE_DISPATCH)
|
||||
#define BIT_DISPATCH
|
||||
#endif
|
||||
|
||||
#ifdef BIT_DISPATCH
|
||||
#include <immintrin.h>
|
||||
|
||||
#if defined(HAVE__GET_CPUID)
|
||||
#if defined(USE__GET_CPUID)
|
||||
#include <cpuid.h>
|
||||
#else
|
||||
#include <intrin.h>
|
||||
@@ -168,7 +173,7 @@ SupportsAvx512Popcount()
|
||||
{
|
||||
unsigned int exx[4] = {0, 0, 0, 0};
|
||||
|
||||
#if defined(HAVE__GET_CPUID)
|
||||
#if defined(USE__GET_CPUID)
|
||||
__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
|
||||
#else
|
||||
__cpuid(exx, 1);
|
||||
@@ -182,7 +187,7 @@ SupportsAvx512Popcount()
|
||||
if ((_xgetbv(0) & 0xe6) != 0xe6)
|
||||
return false;
|
||||
|
||||
#if defined(HAVE__GET_CPUID)
|
||||
#if defined(USE__GET_CPUID)
|
||||
__get_cpuid_count(7, 0, &exx[0], &exx[1], &exx[2], &exx[3]);
|
||||
#else
|
||||
__cpuidex(exx, 7, 0);
|
||||
|
||||
@@ -1,26 +1,11 @@
|
||||
#ifndef BITUTILS_H
|
||||
#define BITUTILS_H
|
||||
|
||||
/* We use two types of dispatching: intrinsics and target_clones */
|
||||
/* TODO Move to better place */
|
||||
#ifndef DISABLE_DISPATCH
|
||||
/* Only enable for more recent compilers to keep build process simple */
|
||||
#if defined(__x86_64__) && defined(__GNUC__) && __GNUC__ >= 8
|
||||
#define USE_DISPATCH
|
||||
#elif defined(__x86_64__) && defined(__clang_major__) && __clang_major__ >= 7
|
||||
#define USE_DISPATCH
|
||||
#elif defined(_M_AMD64) && defined(_MSC_VER) && _MSC_VER >= 1920
|
||||
#define USE_DISPATCH
|
||||
#endif
|
||||
#endif
|
||||
#include "postgres.h"
|
||||
|
||||
/* target_clones requires glibc */
|
||||
#if defined(USE_DISPATCH) && defined(__gnu_linux__)
|
||||
#define USE_TARGET_CLONES
|
||||
#endif
|
||||
|
||||
#if defined(USE_DISPATCH)
|
||||
#define BIT_DISPATCH
|
||||
/* Check version in first header */
|
||||
#if PG_VERSION_NUM < 120000
|
||||
#error "Requires PostgreSQL 12+"
|
||||
#endif
|
||||
|
||||
extern uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "bitutils.h"
|
||||
#include "bitvec.h"
|
||||
#include "utils/varbit.h"
|
||||
#include "vector.h"
|
||||
|
||||
#if PG_VERSION_NUM >= 160000
|
||||
#include "varatt.h"
|
||||
@@ -40,7 +41,7 @@ CheckDims(VarBit *a, VarBit *b)
|
||||
/*
|
||||
* Get the Hamming distance between two bit vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(hamming_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hamming_distance);
|
||||
Datum
|
||||
hamming_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -55,7 +56,7 @@ hamming_distance(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the Jaccard distance between two bit vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(jaccard_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(jaccard_distance);
|
||||
Datum
|
||||
jaccard_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef BITVECTOR_H
|
||||
#define BITVECTOR_H
|
||||
#ifndef BITVEC_H
|
||||
#define BITVEC_H
|
||||
|
||||
#include "utils/varbit.h"
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#ifdef HALFVEC_DISPATCH
|
||||
#include <immintrin.h>
|
||||
|
||||
#if defined(HAVE__GET_CPUID)
|
||||
#if defined(USE__GET_CPUID)
|
||||
#include <cpuid.h>
|
||||
#else
|
||||
#include <intrin.h>
|
||||
@@ -254,7 +254,7 @@ SupportsCpuFeature(unsigned int feature)
|
||||
{
|
||||
unsigned int exx[4] = {0, 0, 0, 0};
|
||||
|
||||
#if defined(HAVE__GET_CPUID)
|
||||
#if defined(USE__GET_CPUID)
|
||||
__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
|
||||
#else
|
||||
__cpuid(exx, 1);
|
||||
|
||||
@@ -67,9 +67,6 @@ HalfToFloat4(half num)
|
||||
#elif defined(FLT16_SUPPORT)
|
||||
return (float) num;
|
||||
#else
|
||||
/* TODO Improve performance */
|
||||
|
||||
/* Assumes same endianness for floats and integers */
|
||||
union
|
||||
{
|
||||
float f;
|
||||
@@ -154,9 +151,6 @@ Float4ToHalfUnchecked(float num)
|
||||
#elif defined(FLT16_SUPPORT)
|
||||
return (_Float16) num;
|
||||
#else
|
||||
/* TODO Improve performance */
|
||||
|
||||
/* Assumes same endianness for floats and integers */
|
||||
union
|
||||
{
|
||||
float f;
|
||||
|
||||
103
src/halfvec.c
103
src/halfvec.c
@@ -185,7 +185,7 @@ float_underflow_error(void)
|
||||
/*
|
||||
* Convert textual representation to internal representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_in);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_in);
|
||||
Datum
|
||||
halfvec_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -299,7 +299,7 @@ halfvec_in(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert internal representation to textual representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_out);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_out);
|
||||
Datum
|
||||
halfvec_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -328,6 +328,10 @@ halfvec_out(PG_FUNCTION_ARGS)
|
||||
if (i > 0)
|
||||
AppendChar(ptr, ',');
|
||||
|
||||
/*
|
||||
* Use shortest decimal representation of single-precision float for
|
||||
* simplicity
|
||||
*/
|
||||
AppendFloat(ptr, HalfToFloat4(vector->x[i]));
|
||||
}
|
||||
|
||||
@@ -341,7 +345,7 @@ halfvec_out(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert type modifier
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_typmod_in);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_typmod_in);
|
||||
Datum
|
||||
halfvec_typmod_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -372,7 +376,7 @@ halfvec_typmod_in(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert external binary representation to internal representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_recv);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_recv);
|
||||
Datum
|
||||
halfvec_recv(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -406,7 +410,7 @@ halfvec_recv(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert internal representation to the external binary representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_send);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_send);
|
||||
Datum
|
||||
halfvec_send(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -426,7 +430,7 @@ halfvec_send(PG_FUNCTION_ARGS)
|
||||
* Convert half vector to half vector
|
||||
* This is needed to check the type modifier
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec);
|
||||
Datum
|
||||
halfvec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -441,7 +445,7 @@ halfvec(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert array to half vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(array_to_halfvec);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(array_to_halfvec);
|
||||
Datum
|
||||
array_to_halfvec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -515,7 +519,7 @@ array_to_halfvec(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert half vector to float4[]
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_float4);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_to_float4);
|
||||
Datum
|
||||
halfvec_to_float4(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -539,7 +543,7 @@ halfvec_to_float4(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert vector to half vec
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_halfvec);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_to_halfvec);
|
||||
Datum
|
||||
vector_to_halfvec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -561,7 +565,7 @@ vector_to_halfvec(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the L2 distance between half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_distance);
|
||||
Datum
|
||||
halfvec_l2_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -576,7 +580,7 @@ halfvec_l2_distance(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the L2 squared distance between half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_squared_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_squared_distance);
|
||||
Datum
|
||||
halfvec_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -591,7 +595,7 @@ halfvec_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the inner product of two half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_inner_product);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_inner_product);
|
||||
Datum
|
||||
halfvec_inner_product(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -606,7 +610,7 @@ halfvec_inner_product(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the negative inner product of two half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_negative_inner_product);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_negative_inner_product);
|
||||
Datum
|
||||
halfvec_negative_inner_product(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -621,7 +625,7 @@ halfvec_negative_inner_product(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the cosine distance between two half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_cosine_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_cosine_distance);
|
||||
Datum
|
||||
halfvec_cosine_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -653,7 +657,7 @@ halfvec_cosine_distance(PG_FUNCTION_ARGS)
|
||||
* Currently uses angular distance since needs to satisfy triangle inequality
|
||||
* Assumes inputs are unit vectors (skips norm)
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_spherical_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_spherical_distance);
|
||||
Datum
|
||||
halfvec_spherical_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -677,7 +681,7 @@ halfvec_spherical_distance(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the L1 distance between two half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l1_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l1_distance);
|
||||
Datum
|
||||
halfvec_l1_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -692,7 +696,7 @@ halfvec_l1_distance(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the dimensions of a half vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_vector_dims);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_vector_dims);
|
||||
Datum
|
||||
halfvec_vector_dims(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -704,7 +708,7 @@ halfvec_vector_dims(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the L2 norm of a half vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_norm);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_norm);
|
||||
Datum
|
||||
halfvec_l2_norm(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -726,7 +730,7 @@ halfvec_l2_norm(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Normalize a half vector with the L2 norm
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_normalize);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_normalize);
|
||||
Datum
|
||||
halfvec_l2_normalize(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -765,7 +769,7 @@ halfvec_l2_normalize(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Add half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_add);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_add);
|
||||
Datum
|
||||
halfvec_add(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -804,7 +808,7 @@ halfvec_add(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Subtract half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_sub);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_sub);
|
||||
Datum
|
||||
halfvec_sub(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -843,7 +847,7 @@ halfvec_sub(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Multiply half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_mul);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_mul);
|
||||
Datum
|
||||
halfvec_mul(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -885,7 +889,7 @@ halfvec_mul(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Concatenate half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_concat);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_concat);
|
||||
Datum
|
||||
halfvec_concat(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -909,7 +913,7 @@ halfvec_concat(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Quantize a half vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_binary_quantize);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_binary_quantize);
|
||||
Datum
|
||||
halfvec_binary_quantize(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -927,24 +931,39 @@ halfvec_binary_quantize(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get a subvector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_subvector);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_subvector);
|
||||
Datum
|
||||
halfvec_subvector(PG_FUNCTION_ARGS)
|
||||
{
|
||||
HalfVector *a = PG_GETARG_HALFVEC_P(0);
|
||||
int32 start = PG_GETARG_INT32(1);
|
||||
int32 count = PG_GETARG_INT32(2);
|
||||
int32 end = start + count;
|
||||
int32 end;
|
||||
half *ax = a->x;
|
||||
HalfVector *result;
|
||||
int dim;
|
||||
int32 dim;
|
||||
|
||||
if (count < 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||
errmsg("halfvec must have at least 1 dimension")));
|
||||
|
||||
/*
|
||||
* Check if (start + count > a->dim), avoiding integer overflow. a->dim
|
||||
* and count are both positive, so a->dim - count won't overflow.
|
||||
*/
|
||||
if (start > a->dim - count)
|
||||
end = a->dim + 1;
|
||||
else
|
||||
end = start + count;
|
||||
|
||||
/* Indexing starts at 1, like substring */
|
||||
if (start < 1)
|
||||
start = 1;
|
||||
|
||||
if (end > a->dim)
|
||||
end = a->dim + 1;
|
||||
else if (start > a->dim)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||
errmsg("halfvec must have at least 1 dimension")));
|
||||
|
||||
dim = end - start;
|
||||
CheckDim(dim);
|
||||
@@ -959,7 +978,7 @@ halfvec_subvector(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Internal helper to compare half vectors
|
||||
*/
|
||||
int
|
||||
static int
|
||||
halfvec_cmp_internal(HalfVector * a, HalfVector * b)
|
||||
{
|
||||
int dim = Min(a->dim, b->dim);
|
||||
@@ -986,7 +1005,7 @@ halfvec_cmp_internal(HalfVector * a, HalfVector * b)
|
||||
/*
|
||||
* Less than
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_lt);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_lt);
|
||||
Datum
|
||||
halfvec_lt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -999,7 +1018,7 @@ halfvec_lt(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Less than or equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_le);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_le);
|
||||
Datum
|
||||
halfvec_le(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1012,7 +1031,7 @@ halfvec_le(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_eq);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_eq);
|
||||
Datum
|
||||
halfvec_eq(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1025,7 +1044,7 @@ halfvec_eq(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Not equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ne);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_ne);
|
||||
Datum
|
||||
halfvec_ne(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1038,7 +1057,7 @@ halfvec_ne(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Greater than or equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ge);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_ge);
|
||||
Datum
|
||||
halfvec_ge(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1051,7 +1070,7 @@ halfvec_ge(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Greater than
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_gt);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_gt);
|
||||
Datum
|
||||
halfvec_gt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1064,7 +1083,7 @@ halfvec_gt(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Compare half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_cmp);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_cmp);
|
||||
Datum
|
||||
halfvec_cmp(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1077,7 +1096,7 @@ halfvec_cmp(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Accumulate half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_accum);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_accum);
|
||||
Datum
|
||||
halfvec_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1138,7 +1157,7 @@ halfvec_accum(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Average half vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_avg);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_avg);
|
||||
Datum
|
||||
halfvec_avg(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1172,7 +1191,7 @@ halfvec_avg(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert sparse vector to half vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_to_halfvec);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_to_halfvec);
|
||||
Datum
|
||||
sparsevec_to_halfvec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1187,7 +1206,7 @@ sparsevec_to_halfvec(PG_FUNCTION_ARGS)
|
||||
|
||||
result = InitHalfVector(dim);
|
||||
for (int i = 0; i < svec->nnz; i++)
|
||||
result->x[svec->indices[i] - 1] = Float4ToHalf(values[i]);
|
||||
result->x[svec->indices[i]] = Float4ToHalf(values[i]);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,31 @@
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#include "bitutils.h"
|
||||
#include "fmgr.h"
|
||||
#include "vector.h"
|
||||
/* We use two types of dispatching: intrinsics and target_clones */
|
||||
/* TODO Move to better place */
|
||||
#ifndef DISABLE_DISPATCH
|
||||
/* Only enable for more recent compilers to keep build process simple */
|
||||
#if defined(__x86_64__) && defined(__GNUC__) && __GNUC__ >= 9
|
||||
#define USE_DISPATCH
|
||||
#elif defined(__x86_64__) && defined(__clang_major__) && __clang_major__ >= 7
|
||||
#define USE_DISPATCH
|
||||
#elif defined(_M_AMD64) && defined(_MSC_VER) && _MSC_VER >= 1920
|
||||
#define USE_DISPATCH
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* target_clones requires glibc */
|
||||
#if defined(USE_DISPATCH) && defined(__gnu_linux__) && defined(__has_attribute)
|
||||
/* Use separate line for portability */
|
||||
#if __has_attribute(target_clones)
|
||||
#define USE_TARGET_CLONES
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Apple clang check needed for universal binaries on Mac */
|
||||
#if defined(USE_DISPATCH) && (defined(HAVE__GET_CPUID) || defined(__apple_build_version__))
|
||||
#define USE__GET_CPUID
|
||||
#endif
|
||||
|
||||
#if defined(USE_DISPATCH)
|
||||
#define HALFVEC_DISPATCH
|
||||
@@ -16,7 +38,7 @@
|
||||
/* F16C has better performance than _Float16 (on x86-64) */
|
||||
#if defined(__F16C__)
|
||||
#define F16C_SUPPORT
|
||||
#elif defined(__FLT16_MAX__) && !defined(HALFVEC_DISPATCH)
|
||||
#elif defined(__FLT16_MAX__) && !defined(HALFVEC_DISPATCH) && !defined(__FreeBSD__) && (!defined(__i386__) || defined(__SSE2__))
|
||||
#define FLT16_SUPPORT
|
||||
#endif
|
||||
|
||||
@@ -28,7 +50,7 @@
|
||||
#define HALF_MAX 65504
|
||||
#endif
|
||||
|
||||
#define HALFVEC_MAX_DIM VECTOR_MAX_DIM
|
||||
#define HALFVEC_MAX_DIM 16000
|
||||
|
||||
#define HALFVEC_SIZE(_dim) (offsetof(HalfVector, x) + sizeof(half)*(_dim))
|
||||
#define DatumGetHalfVector(x) ((HalfVector *) PG_DETOAST_DATUM(x))
|
||||
@@ -39,11 +61,10 @@ typedef struct HalfVector
|
||||
{
|
||||
int32 vl_len_; /* varlena header (do not touch directly!) */
|
||||
int16 dim; /* number of dimensions */
|
||||
int16 unused;
|
||||
int16 unused; /* reserved for future use, always zero */
|
||||
half x[FLEXIBLE_ARRAY_MEMBER];
|
||||
} HalfVector;
|
||||
|
||||
HalfVector *InitHalfVector(int dim);
|
||||
int halfvec_cmp_internal(HalfVector * a, HalfVector * b);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -187,14 +187,14 @@ hnswvalidate(Oid opclassoid)
|
||||
*
|
||||
* See https://www.postgresql.org/docs/current/index-api.html
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(hnswhandler);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnswhandler);
|
||||
Datum
|
||||
hnswhandler(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
|
||||
|
||||
amroutine->amstrategies = 0;
|
||||
amroutine->amsupport = 4;
|
||||
amroutine->amsupport = 3;
|
||||
#if PG_VERSION_NUM >= 130000
|
||||
amroutine->amoptsprocnum = 0;
|
||||
#endif
|
||||
|
||||
35
src/hnsw.h
35
src/hnsw.h
@@ -12,18 +12,13 @@
|
||||
#include "utils/sampling.h"
|
||||
#include "vector.h"
|
||||
|
||||
#if PG_VERSION_NUM < 120000
|
||||
#error "Requires PostgreSQL 12+"
|
||||
#endif
|
||||
|
||||
#define HNSW_MAX_DIM 2000
|
||||
#define HNSW_MAX_NNZ 1000
|
||||
|
||||
/* Support functions */
|
||||
#define HNSW_DISTANCE_PROC 1
|
||||
#define HNSW_NORM_PROC 2
|
||||
#define HNSW_NORMALIZE_PROC 3
|
||||
#define HNSW_TYPE_SUPPORT_PROC 4
|
||||
#define HNSW_TYPE_INFO_PROC 3
|
||||
|
||||
#define HNSW_VERSION 1
|
||||
#define HNSW_MAGIC_NUMBER 0xA953A953
|
||||
@@ -58,15 +53,6 @@
|
||||
#define HNSW_UPDATE_ENTRY_GREATER 1
|
||||
#define HNSW_UPDATE_ENTRY_ALWAYS 2
|
||||
|
||||
typedef enum HnswType
|
||||
{
|
||||
HNSW_TYPE_VECTOR,
|
||||
HNSW_TYPE_HALFVEC,
|
||||
HNSW_TYPE_BIT,
|
||||
HNSW_TYPE_SPARSEVEC,
|
||||
HNSW_TYPE_UNSUPPORTED
|
||||
} HnswType;
|
||||
|
||||
/* Build phases */
|
||||
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
|
||||
#define PROGRESS_HNSW_PHASE_LOAD 2
|
||||
@@ -247,6 +233,13 @@ typedef struct HnswAllocator
|
||||
void *state;
|
||||
} HnswAllocator;
|
||||
|
||||
typedef struct HnswTypeInfo
|
||||
{
|
||||
int maxDimensions;
|
||||
Datum (*normalize) (PG_FUNCTION_ARGS);
|
||||
void (*checkValue) (Pointer v);
|
||||
} HnswTypeInfo;
|
||||
|
||||
typedef struct HnswBuildState
|
||||
{
|
||||
/* Info */
|
||||
@@ -254,7 +247,7 @@ typedef struct HnswBuildState
|
||||
Relation index;
|
||||
IndexInfo *indexInfo;
|
||||
ForkNumber forkNum;
|
||||
HnswType type;
|
||||
const HnswTypeInfo *typeInfo;
|
||||
|
||||
/* Settings */
|
||||
int dimensions;
|
||||
@@ -268,7 +261,6 @@ typedef struct HnswBuildState
|
||||
/* Support functions */
|
||||
FmgrInfo *procinfo;
|
||||
FmgrInfo *normprocinfo;
|
||||
FmgrInfo *normalizeprocinfo;
|
||||
Oid collation;
|
||||
|
||||
/* Variables */
|
||||
@@ -338,6 +330,7 @@ typedef HnswNeighborTupleData * HnswNeighborTuple;
|
||||
|
||||
typedef struct HnswScanOpaqueData
|
||||
{
|
||||
const HnswTypeInfo *typeInfo;
|
||||
bool first;
|
||||
List *w;
|
||||
MemoryContext tmpCtx;
|
||||
@@ -345,7 +338,6 @@ typedef struct HnswScanOpaqueData
|
||||
/* Support functions */
|
||||
FmgrInfo *procinfo;
|
||||
FmgrInfo *normprocinfo;
|
||||
FmgrInfo *normalizeprocinfo;
|
||||
Oid collation;
|
||||
} HnswScanOpaqueData;
|
||||
|
||||
@@ -381,10 +373,8 @@ typedef struct HnswVacuumState
|
||||
int HnswGetM(Relation index);
|
||||
int HnswGetEfConstruction(Relation index);
|
||||
FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum);
|
||||
HnswType HnswGetType(Relation index);
|
||||
Datum HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum value);
|
||||
Datum HnswNormValue(const HnswTypeInfo * typeInfo, Oid collation, Datum value);
|
||||
bool HnswCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value);
|
||||
void HnswCheckValue(Datum value, HnswType type);
|
||||
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
|
||||
void HnswInitPage(Buffer buf, Page page);
|
||||
void HnswInit(void);
|
||||
@@ -403,11 +393,12 @@ void HnswInitNeighbors(char *base, HnswElement element, int m, HnswAllocator *
|
||||
bool HnswInsertTupleOnDisk(Relation index, Datum value, Datum *values, bool *isnull, ItemPointer heap_tid, bool building);
|
||||
void HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting, bool building);
|
||||
void HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHeaptids, bool loadVec);
|
||||
void HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
||||
void HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec, float *maxDistance);
|
||||
void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element);
|
||||
void HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm, int lc, int *updateIdx, Relation index, FmgrInfo *procinfo, Oid collation);
|
||||
void HnswLoadNeighbors(HnswElement element, Relation index, int m);
|
||||
void HnswInitLockTranche(void);
|
||||
const HnswTypeInfo *HnswGetTypeInfo(Relation index);
|
||||
PGDLLEXPORT void HnswParallelBuildMain(dsm_segment *seg, shm_toc *toc);
|
||||
|
||||
/* Index access methods */
|
||||
|
||||
@@ -379,7 +379,13 @@ UpdateNeighborsInMemory(char *base, FmgrInfo *procinfo, Oid collation, HnswEleme
|
||||
for (int lc = e->level; lc >= 0; lc--)
|
||||
{
|
||||
int lm = HnswGetLayerM(m, lc);
|
||||
HnswNeighborArray *neighbors = HnswGetNeighbors(base, e, lc);
|
||||
Size neighborsSize = HNSW_NEIGHBOR_ARRAY_SIZE(lm);
|
||||
HnswNeighborArray *neighbors = palloc(neighborsSize);
|
||||
|
||||
/* Copy neighbors to local memory */
|
||||
LWLockAcquire(&e->lock, LW_SHARED);
|
||||
memcpy(neighbors, HnswGetNeighbors(base, e, lc), neighborsSize);
|
||||
LWLockRelease(&e->lock);
|
||||
|
||||
for (int i = 0; i < neighbors->length; i++)
|
||||
{
|
||||
@@ -389,7 +395,6 @@ UpdateNeighborsInMemory(char *base, FmgrInfo *procinfo, Oid collation, HnswEleme
|
||||
/* Keep scan-build happy on Mac x86-64 */
|
||||
Assert(neighborElement);
|
||||
|
||||
/* Use element for lock instead of hc since hc can be replaced */
|
||||
LWLockAcquire(&neighborElement->lock, LW_EXCLUSIVE);
|
||||
HnswUpdateConnection(base, e, hc, lm, lc, NULL, NULL, procinfo, collation);
|
||||
LWLockRelease(&neighborElement->lock);
|
||||
@@ -476,6 +481,7 @@ InsertTupleInMemory(HnswBuildState * buildstate, HnswElement element)
|
||||
static bool
|
||||
InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, HnswBuildState * buildstate)
|
||||
{
|
||||
const HnswTypeInfo *typeInfo = buildstate->typeInfo;
|
||||
HnswGraph *graph = buildstate->graph;
|
||||
HnswElement element;
|
||||
HnswAllocator *allocator = &buildstate->allocator;
|
||||
@@ -488,7 +494,8 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
|
||||
Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
|
||||
|
||||
/* Check value */
|
||||
HnswCheckValue(value, buildstate->type);
|
||||
if (typeInfo->checkValue != NULL)
|
||||
typeInfo->checkValue(DatumGetPointer(value));
|
||||
|
||||
/* Normalize if needed */
|
||||
if (buildstate->normprocinfo != NULL)
|
||||
@@ -496,7 +503,7 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
|
||||
if (!HnswCheckNorm(buildstate->normprocinfo, buildstate->collation, value))
|
||||
return false;
|
||||
|
||||
value = HnswNormValue(buildstate->normalizeprocinfo, buildstate->collation, value);
|
||||
value = HnswNormValue(typeInfo, buildstate->collation, value);
|
||||
}
|
||||
|
||||
/* Get datum size */
|
||||
@@ -671,50 +678,32 @@ HnswSharedMemoryAlloc(Size size, void *state)
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get max dimensions
|
||||
*/
|
||||
static int
|
||||
GetMaxDimensions(HnswType type)
|
||||
{
|
||||
int maxDimensions = HNSW_MAX_DIM;
|
||||
|
||||
if (type == HNSW_TYPE_HALFVEC)
|
||||
maxDimensions *= 2;
|
||||
else if (type == HNSW_TYPE_BIT)
|
||||
maxDimensions *= 32;
|
||||
else if (type == HNSW_TYPE_SPARSEVEC)
|
||||
maxDimensions = INT_MAX;
|
||||
|
||||
return maxDimensions;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the build state
|
||||
*/
|
||||
static void
|
||||
InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo, ForkNumber forkNum)
|
||||
{
|
||||
int maxDimensions;
|
||||
|
||||
buildstate->heap = heap;
|
||||
buildstate->index = index;
|
||||
buildstate->indexInfo = indexInfo;
|
||||
buildstate->forkNum = forkNum;
|
||||
buildstate->type = HnswGetType(index);
|
||||
buildstate->typeInfo = HnswGetTypeInfo(index);
|
||||
|
||||
buildstate->m = HnswGetM(index);
|
||||
buildstate->efConstruction = HnswGetEfConstruction(index);
|
||||
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
|
||||
|
||||
maxDimensions = GetMaxDimensions(buildstate->type);
|
||||
/* Disallow varbit since require fixed dimensions */
|
||||
if (TupleDescAttr(index->rd_att, 0)->atttypid == VARBITOID)
|
||||
elog(ERROR, "type not supported for hnsw index");
|
||||
|
||||
/* Require column to have dimensions to be indexed */
|
||||
if (buildstate->dimensions < 0)
|
||||
elog(ERROR, "column does not have dimensions");
|
||||
|
||||
if (buildstate->dimensions > maxDimensions)
|
||||
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", maxDimensions);
|
||||
if (buildstate->dimensions > buildstate->typeInfo->maxDimensions)
|
||||
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", buildstate->typeInfo->maxDimensions);
|
||||
|
||||
if (buildstate->efConstruction < 2 * buildstate->m)
|
||||
elog(ERROR, "ef_construction must be greater than or equal to 2 * m");
|
||||
@@ -725,7 +714,6 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index
|
||||
/* Get support functions */
|
||||
buildstate->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC);
|
||||
buildstate->normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
|
||||
buildstate->normalizeprocinfo = HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC);
|
||||
buildstate->collation = index->rd_indcollation[0];
|
||||
|
||||
InitGraph(&buildstate->graphData, NULL, maintenance_work_mem * 1024L);
|
||||
@@ -1138,8 +1126,8 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo,
|
||||
|
||||
BuildGraph(buildstate, forkNum);
|
||||
|
||||
if (RelationNeedsWAL(index))
|
||||
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocks(index), true);
|
||||
if (RelationNeedsWAL(index) || forkNum == INIT_FORKNUM)
|
||||
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocksInFork(index, forkNum), true);
|
||||
|
||||
FreeBuildState(buildstate);
|
||||
}
|
||||
|
||||
@@ -36,14 +36,15 @@ GetInsertPage(Relation index)
|
||||
* Check for a free offset
|
||||
*/
|
||||
static bool
|
||||
HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size ntupSize, Buffer *nbuf, Page *npage, OffsetNumber *freeOffno, OffsetNumber *freeNeighborOffno, BlockNumber *newInsertPage)
|
||||
HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size etupSize, Size ntupSize, Buffer *nbuf, Page *npage, OffsetNumber *freeOffno, OffsetNumber *freeNeighborOffno, BlockNumber *newInsertPage)
|
||||
{
|
||||
OffsetNumber offno;
|
||||
OffsetNumber maxoffno = PageGetMaxOffsetNumber(page);
|
||||
|
||||
for (offno = FirstOffsetNumber; offno <= maxoffno; offno = OffsetNumberNext(offno))
|
||||
{
|
||||
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, PageGetItemId(page, offno));
|
||||
ItemId eitemid = PageGetItemId(page, offno);
|
||||
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, eitemid);
|
||||
|
||||
/* Skip neighbor tuples */
|
||||
if (!HnswIsElementTuple(etup))
|
||||
@@ -54,7 +55,9 @@ HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size
|
||||
BlockNumber elementPage = BufferGetBlockNumber(buf);
|
||||
BlockNumber neighborPage = ItemPointerGetBlockNumber(&etup->neighbortid);
|
||||
OffsetNumber neighborOffno = ItemPointerGetOffsetNumber(&etup->neighbortid);
|
||||
ItemId itemid;
|
||||
ItemId nitemid;
|
||||
Size pageFree;
|
||||
Size npageFree;
|
||||
|
||||
if (!BlockNumberIsValid(*newInsertPage))
|
||||
*newInsertPage = elementPage;
|
||||
@@ -73,10 +76,25 @@ HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size
|
||||
*npage = BufferGetPage(*nbuf);
|
||||
}
|
||||
|
||||
itemid = PageGetItemId(*npage, neighborOffno);
|
||||
nitemid = PageGetItemId(*npage, neighborOffno);
|
||||
|
||||
/* Check for space on neighbor tuple page */
|
||||
if (PageGetFreeSpace(*npage) + ItemIdGetLength(itemid) - sizeof(ItemIdData) >= ntupSize)
|
||||
/* Ensure aligned for space check */
|
||||
Assert(etupSize == MAXALIGN(etupSize));
|
||||
Assert(ntupSize == MAXALIGN(ntupSize));
|
||||
|
||||
/*
|
||||
* Calculate free space individually since tuples are overwritten
|
||||
* individually (in separate calls to PageIndexTupleOverwrite)
|
||||
*/
|
||||
pageFree = ItemIdGetLength(eitemid) + PageGetExactFreeSpace(page);
|
||||
npageFree = ItemIdGetLength(nitemid);
|
||||
if (neighborPage != elementPage)
|
||||
npageFree += PageGetExactFreeSpace(*npage);
|
||||
else if (pageFree >= etupSize)
|
||||
npageFree += pageFree - etupSize;
|
||||
|
||||
/* Check for space */
|
||||
if (pageFree >= etupSize && npageFree >= ntupSize)
|
||||
{
|
||||
*freeOffno = offno;
|
||||
*freeNeighborOffno = neighborOffno;
|
||||
@@ -184,7 +202,7 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
|
||||
}
|
||||
|
||||
/* Next, try space from a deleted element */
|
||||
if (HnswFreeOffset(index, buf, page, e, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage))
|
||||
if (HnswFreeOffset(index, buf, page, e, etupSize, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage))
|
||||
{
|
||||
if (nbuf != buf)
|
||||
{
|
||||
@@ -612,6 +630,7 @@ static void
|
||||
HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid)
|
||||
{
|
||||
Datum value;
|
||||
const HnswTypeInfo *typeInfo = HnswGetTypeInfo(index);
|
||||
FmgrInfo *normprocinfo;
|
||||
Oid collation = index->rd_indcollation[0];
|
||||
|
||||
@@ -619,7 +638,8 @@ HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_ti
|
||||
value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
|
||||
|
||||
/* Check value */
|
||||
HnswCheckValue(value, HnswGetType(index));
|
||||
if (typeInfo->checkValue != NULL)
|
||||
typeInfo->checkValue(DatumGetPointer(value));
|
||||
|
||||
/* Normalize if needed */
|
||||
normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
|
||||
@@ -628,7 +648,7 @@ HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_ti
|
||||
if (!HnswCheckNorm(normprocinfo, collation, value))
|
||||
return;
|
||||
|
||||
value = HnswNormValue(HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC), collation, value);
|
||||
value = HnswNormValue(typeInfo, collation, value);
|
||||
}
|
||||
|
||||
HnswInsertTupleOnDisk(index, value, values, isnull, heap_tid, false);
|
||||
|
||||
@@ -59,9 +59,9 @@ GetScanValue(IndexScanDesc scan)
|
||||
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
|
||||
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
|
||||
|
||||
/* Fine if normalization fails */
|
||||
/* Normalize if needed */
|
||||
if (so->normprocinfo != NULL)
|
||||
value = HnswNormValue(so->normalizeprocinfo, so->collation, value);
|
||||
value = HnswNormValue(so->typeInfo, so->collation, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -79,6 +79,7 @@ hnswbeginscan(Relation index, int nkeys, int norderbys)
|
||||
scan = RelationGetIndexScan(index, nkeys, norderbys);
|
||||
|
||||
so = (HnswScanOpaque) palloc(sizeof(HnswScanOpaqueData));
|
||||
so->typeInfo = HnswGetTypeInfo(index);
|
||||
so->first = true;
|
||||
so->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"Hnsw scan temporary context",
|
||||
@@ -87,7 +88,6 @@ hnswbeginscan(Relation index, int nkeys, int norderbys)
|
||||
/* Set support functions */
|
||||
so->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC);
|
||||
so->normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
|
||||
so->normalizeprocinfo = HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC);
|
||||
so->collation = index->rd_indcollation[0];
|
||||
|
||||
scan->opaque = so;
|
||||
|
||||
161
src/hnswutils.c
161
src/hnswutils.c
@@ -152,37 +152,13 @@ HnswOptionalProcInfo(Relation index, uint16 procnum)
|
||||
return index_getprocinfo(index, 1, procnum);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get type
|
||||
*/
|
||||
HnswType
|
||||
HnswGetType(Relation index)
|
||||
{
|
||||
FmgrInfo *procinfo = HnswOptionalProcInfo(index, HNSW_TYPE_SUPPORT_PROC);
|
||||
Oid typid = TupleDescAttr(index->rd_att, 0)->atttypid;
|
||||
HnswType result;
|
||||
|
||||
if (procinfo == NULL)
|
||||
return HNSW_TYPE_VECTOR;
|
||||
|
||||
result = (HnswType) DatumGetInt32(FunctionCall1(procinfo, ObjectIdGetDatum(typid)));
|
||||
|
||||
if (result == HNSW_TYPE_UNSUPPORTED)
|
||||
elog(ERROR, "type not supported for hnsw index");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalize value
|
||||
*/
|
||||
Datum
|
||||
HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum value)
|
||||
HnswNormValue(const HnswTypeInfo * typeInfo, Oid collation, Datum value)
|
||||
{
|
||||
if (procinfo == NULL)
|
||||
return DirectFunctionCall1(l2_normalize, value);
|
||||
|
||||
return FunctionCall1Coll(procinfo, collation, value);
|
||||
return DirectFunctionCall1Coll(typeInfo->normalize, collation, value);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -194,21 +170,6 @@ HnswCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value)
|
||||
return DatumGetFloat8(FunctionCall1Coll(procinfo, collation, value)) > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a value can be indexed
|
||||
*/
|
||||
void
|
||||
HnswCheckValue(Datum value, HnswType type)
|
||||
{
|
||||
if (type == HNSW_TYPE_SPARSEVEC)
|
||||
{
|
||||
SparseVector *vec = DatumGetSparseVector(value);
|
||||
|
||||
if (vec->nnz > HNSW_MAX_NNZ)
|
||||
elog(ERROR, "sparsevec cannot have more than %d non-zero elements for hnsw index", HNSW_MAX_NNZ);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* New buffer
|
||||
*/
|
||||
@@ -339,6 +300,9 @@ HnswGetMetaPageInfo(Relation index, int *m, HnswElement * entryPoint)
|
||||
page = BufferGetPage(buf);
|
||||
metap = HnswPageGetMeta(page);
|
||||
|
||||
if (unlikely(metap->magicNumber != HNSW_MAGIC_NUMBER))
|
||||
elog(ERROR, "hnsw index is not valid");
|
||||
|
||||
if (m != NULL)
|
||||
*m = metap->m;
|
||||
|
||||
@@ -584,7 +548,7 @@ HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHe
|
||||
* Load an element and optionally get its distance from q
|
||||
*/
|
||||
void
|
||||
HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec)
|
||||
HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec, float *maxDistance)
|
||||
{
|
||||
Buffer buf;
|
||||
Page page;
|
||||
@@ -599,9 +563,6 @@ HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index,
|
||||
|
||||
Assert(HnswIsElementTuple(etup));
|
||||
|
||||
/* Load element */
|
||||
HnswLoadElementFromTuple(element, etup, true, loadVec);
|
||||
|
||||
/* Calculate distance */
|
||||
if (distance != NULL)
|
||||
{
|
||||
@@ -611,6 +572,10 @@ HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index,
|
||||
*distance = (float) DatumGetFloat8(FunctionCall2Coll(procinfo, collation, *q, PointerGetDatum(&etup->data)));
|
||||
}
|
||||
|
||||
/* Load element */
|
||||
if (distance == NULL || maxDistance == NULL || *distance < *maxDistance)
|
||||
HnswLoadElementFromTuple(element, etup, true, loadVec);
|
||||
|
||||
UnlockReleaseBuffer(buf);
|
||||
}
|
||||
|
||||
@@ -638,7 +603,7 @@ HnswEntryCandidate(char *base, HnswElement entryPoint, Datum q, Relation index,
|
||||
if (index == NULL)
|
||||
hc->distance = GetCandidateDistance(base, hc, q, procinfo, collation);
|
||||
else
|
||||
HnswLoadElement(entryPoint, &hc->distance, &q, index, procinfo, collation, loadVec);
|
||||
HnswLoadElement(entryPoint, &hc->distance, &q, index, procinfo, collation, loadVec, NULL);
|
||||
return hc;
|
||||
}
|
||||
|
||||
@@ -834,25 +799,27 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
|
||||
{
|
||||
float eDistance;
|
||||
HnswElement eElement = HnswPtrAccess(base, e->element);
|
||||
bool alwaysAdd = wlen < ef;
|
||||
|
||||
f = ((HnswPairingHeapNode *) pairingheap_first(W))->inner;
|
||||
|
||||
if (index == NULL)
|
||||
eDistance = GetCandidateDistance(base, e, q, procinfo, collation);
|
||||
else
|
||||
HnswLoadElement(eElement, &eDistance, &q, index, procinfo, collation, inserting);
|
||||
HnswLoadElement(eElement, &eDistance, &q, index, procinfo, collation, inserting, alwaysAdd ? NULL : &f->distance);
|
||||
|
||||
Assert(!eElement->deleted);
|
||||
|
||||
/* Make robust to issues */
|
||||
if (eElement->level < lc)
|
||||
continue;
|
||||
|
||||
if (eDistance < f->distance || wlen < ef)
|
||||
if (eDistance < f->distance || alwaysAdd)
|
||||
{
|
||||
/* Copy e */
|
||||
HnswCandidate *ec = palloc(sizeof(HnswCandidate));
|
||||
HnswCandidate *ec;
|
||||
|
||||
Assert(!eElement->deleted);
|
||||
|
||||
/* Make robust to issues */
|
||||
if (eElement->level < lc)
|
||||
continue;
|
||||
|
||||
/* Copy e */
|
||||
ec = palloc(sizeof(HnswCandidate));
|
||||
HnswPtrStore(base, ec->element, eElement);
|
||||
ec->distance = eDistance;
|
||||
|
||||
@@ -1141,7 +1108,7 @@ HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm
|
||||
HnswElement hc3Element = HnswPtrAccess(base, hc3->element);
|
||||
|
||||
if (HnswPtrIsNull(base, hc3Element->value))
|
||||
HnswLoadElement(hc3Element, &hc3->distance, &q, index, procinfo, collation, true);
|
||||
HnswLoadElement(hc3Element, &hc3->distance, &q, index, procinfo, collation, true, NULL);
|
||||
else
|
||||
hc3->distance = GetCandidateDistance(base, hc3, q, procinfo, collation);
|
||||
|
||||
@@ -1303,28 +1270,76 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
||||
}
|
||||
}
|
||||
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_hnsw_support);
|
||||
Datum
|
||||
halfvec_hnsw_support(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_INT32(HNSW_TYPE_HALFVEC);
|
||||
};
|
||||
PGDLLEXPORT Datum l2_normalize(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum halfvec_l2_normalize(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum sparsevec_l2_normalize(PG_FUNCTION_ARGS);
|
||||
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(bit_hnsw_support);
|
||||
Datum
|
||||
bit_hnsw_support(PG_FUNCTION_ARGS)
|
||||
static void
|
||||
SparsevecCheckValue(Pointer v)
|
||||
{
|
||||
Oid typid = PG_GETARG_OID(0);
|
||||
SparseVector *vec = (SparseVector *) v;
|
||||
|
||||
if (typid == BITOID)
|
||||
PG_RETURN_INT32(HNSW_TYPE_BIT);
|
||||
if (vec->nnz > HNSW_MAX_NNZ)
|
||||
elog(ERROR, "sparsevec cannot have more than %d non-zero elements for hnsw index", HNSW_MAX_NNZ);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get type info
|
||||
*/
|
||||
const HnswTypeInfo *
|
||||
HnswGetTypeInfo(Relation index)
|
||||
{
|
||||
FmgrInfo *procinfo = HnswOptionalProcInfo(index, HNSW_TYPE_INFO_PROC);
|
||||
|
||||
if (procinfo == NULL)
|
||||
{
|
||||
static const HnswTypeInfo typeInfo = {
|
||||
.maxDimensions = HNSW_MAX_DIM,
|
||||
.normalize = l2_normalize,
|
||||
.checkValue = NULL
|
||||
};
|
||||
|
||||
return (&typeInfo);
|
||||
}
|
||||
else
|
||||
PG_RETURN_INT32(HNSW_TYPE_UNSUPPORTED);
|
||||
return (const HnswTypeInfo *) DatumGetPointer(FunctionCall0Coll(procinfo, InvalidOid));
|
||||
}
|
||||
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnsw_halfvec_support);
|
||||
Datum
|
||||
hnsw_halfvec_support(PG_FUNCTION_ARGS)
|
||||
{
|
||||
static const HnswTypeInfo typeInfo = {
|
||||
.maxDimensions = HNSW_MAX_DIM * 2,
|
||||
.normalize = halfvec_l2_normalize,
|
||||
.checkValue = NULL
|
||||
};
|
||||
|
||||
PG_RETURN_POINTER(&typeInfo);
|
||||
};
|
||||
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_hnsw_support);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnsw_bit_support);
|
||||
Datum
|
||||
sparsevec_hnsw_support(PG_FUNCTION_ARGS)
|
||||
hnsw_bit_support(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_INT32(HNSW_TYPE_SPARSEVEC);
|
||||
static const HnswTypeInfo typeInfo = {
|
||||
.maxDimensions = HNSW_MAX_DIM * 32,
|
||||
.normalize = NULL,
|
||||
.checkValue = NULL
|
||||
};
|
||||
|
||||
PG_RETURN_POINTER(&typeInfo);
|
||||
};
|
||||
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnsw_sparsevec_support);
|
||||
Datum
|
||||
hnsw_sparsevec_support(PG_FUNCTION_ARGS)
|
||||
{
|
||||
static const HnswTypeInfo typeInfo = {
|
||||
.maxDimensions = SPARSEVEC_MAX_DIM,
|
||||
.normalize = sparsevec_l2_normalize,
|
||||
.checkValue = SparsevecCheckValue
|
||||
};
|
||||
|
||||
PG_RETURN_POINTER(&typeInfo);
|
||||
};
|
||||
|
||||
@@ -256,7 +256,7 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
|
||||
LockPage(index, HNSW_UPDATE_LOCK, ShareLock);
|
||||
|
||||
/* Load element */
|
||||
HnswLoadElement(highestPoint, NULL, NULL, index, vacuumstate->procinfo, vacuumstate->collation, true);
|
||||
HnswLoadElement(highestPoint, NULL, NULL, index, vacuumstate->procinfo, vacuumstate->collation, true, NULL);
|
||||
|
||||
/* Repair if needed */
|
||||
if (NeedsUpdated(vacuumstate, highestPoint))
|
||||
@@ -294,7 +294,7 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
|
||||
* is outdated, this can remove connections at higher levels in
|
||||
* the graph until they are repaired, but this should be fine.
|
||||
*/
|
||||
HnswLoadElement(entryPoint, NULL, NULL, index, vacuumstate->procinfo, vacuumstate->collation, true);
|
||||
HnswLoadElement(entryPoint, NULL, NULL, index, vacuumstate->procinfo, vacuumstate->collation, true, NULL);
|
||||
|
||||
if (NeedsUpdated(vacuumstate, entryPoint))
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
|
||||
if (!IvfflatCheckNorm(buildstate->kmeansnormprocinfo, buildstate->collation, value))
|
||||
return;
|
||||
|
||||
value = IvfflatNormValue(buildstate->normalizeprocinfo, buildstate->collation, value);
|
||||
value = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
|
||||
}
|
||||
|
||||
if (samples->length < targsamples)
|
||||
@@ -161,7 +161,7 @@ AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState
|
||||
if (!IvfflatCheckNorm(buildstate->normprocinfo, buildstate->collation, value))
|
||||
return;
|
||||
|
||||
value = IvfflatNormValue(buildstate->normalizeprocinfo, buildstate->collation, value);
|
||||
value = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
|
||||
}
|
||||
|
||||
/* Find the list that minimizes the distance */
|
||||
@@ -319,62 +319,30 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get max dimensions
|
||||
*/
|
||||
static int
|
||||
GetMaxDimensions(IvfflatType type)
|
||||
{
|
||||
int maxDimensions = IVFFLAT_MAX_DIM;
|
||||
|
||||
if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
maxDimensions *= 2;
|
||||
else if (type == IVFFLAT_TYPE_BIT)
|
||||
maxDimensions *= 32;
|
||||
|
||||
return maxDimensions;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get item size
|
||||
*/
|
||||
static Size
|
||||
GetItemSize(IvfflatType type, int dimensions)
|
||||
{
|
||||
if (type == IVFFLAT_TYPE_VECTOR)
|
||||
return VECTOR_SIZE(dimensions);
|
||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
return HALFVEC_SIZE(dimensions);
|
||||
else if (type == IVFFLAT_TYPE_BIT)
|
||||
return VARBITTOTALLEN(dimensions);
|
||||
else
|
||||
elog(ERROR, "Unsupported type");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the build state
|
||||
*/
|
||||
static void
|
||||
InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo)
|
||||
{
|
||||
int maxDimensions;
|
||||
|
||||
buildstate->heap = heap;
|
||||
buildstate->index = index;
|
||||
buildstate->indexInfo = indexInfo;
|
||||
buildstate->type = IvfflatGetType(index);
|
||||
buildstate->typeInfo = IvfflatGetTypeInfo(index);
|
||||
|
||||
buildstate->lists = IvfflatGetLists(index);
|
||||
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
|
||||
|
||||
maxDimensions = GetMaxDimensions(buildstate->type);
|
||||
/* Disallow varbit since require fixed dimensions */
|
||||
if (TupleDescAttr(index->rd_att, 0)->atttypid == VARBITOID)
|
||||
elog(ERROR, "type not supported for ivfflat index");
|
||||
|
||||
/* Require column to have dimensions to be indexed */
|
||||
if (buildstate->dimensions < 0)
|
||||
elog(ERROR, "column does not have dimensions");
|
||||
|
||||
if (buildstate->dimensions > maxDimensions)
|
||||
elog(ERROR, "column cannot have more than %d dimensions for ivfflat index", maxDimensions);
|
||||
if (buildstate->dimensions > buildstate->typeInfo->maxDimensions)
|
||||
elog(ERROR, "column cannot have more than %d dimensions for ivfflat index", buildstate->typeInfo->maxDimensions);
|
||||
|
||||
buildstate->reltuples = 0;
|
||||
buildstate->indtuples = 0;
|
||||
@@ -383,7 +351,6 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
||||
buildstate->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC);
|
||||
buildstate->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
||||
buildstate->kmeansnormprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||
buildstate->normalizeprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC);
|
||||
buildstate->collation = index->rd_indcollation[0];
|
||||
|
||||
/* Require more than one dimension for spherical k-means */
|
||||
@@ -398,7 +365,7 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
||||
|
||||
buildstate->slot = MakeSingleTupleTableSlot(buildstate->tupdesc, &TTSOpsVirtual);
|
||||
|
||||
buildstate->centers = VectorArrayInit(buildstate->lists, buildstate->dimensions, GetItemSize(buildstate->type, buildstate->dimensions));
|
||||
buildstate->centers = VectorArrayInit(buildstate->lists, buildstate->dimensions, buildstate->typeInfo->itemSize(buildstate->dimensions));
|
||||
buildstate->listInfo = palloc(sizeof(ListInfo) * buildstate->lists);
|
||||
|
||||
buildstate->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
@@ -468,7 +435,7 @@ ComputeCenters(IvfflatBuildState * buildstate)
|
||||
}
|
||||
|
||||
/* Calculate centers */
|
||||
IvfflatBench("k-means", IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers, buildstate->type));
|
||||
IvfflatBench("k-means", IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers, buildstate->typeInfo));
|
||||
|
||||
/* Free samples before we allocate more memory */
|
||||
VectorArrayFree(buildstate->samples);
|
||||
@@ -523,10 +490,13 @@ CreateListPages(Relation index, VectorArray centers, int dimensions,
|
||||
{
|
||||
OffsetNumber offno;
|
||||
|
||||
/* Zero memory for each list */
|
||||
MemSet(list, 0, listSize);
|
||||
|
||||
/* Load list */
|
||||
list->startPage = InvalidBlockNumber;
|
||||
list->insertPage = InvalidBlockNumber;
|
||||
memcpy(&list->center, VectorArrayGet(centers, i), centers->itemsize);
|
||||
memcpy(&list->center, VectorArrayGet(centers, i), VARSIZE_ANY(VectorArrayGet(centers, i)));
|
||||
|
||||
/* Ensure free space */
|
||||
if (PageGetFreeSpace(page) < listSize)
|
||||
@@ -1036,6 +1006,10 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo,
|
||||
CreateListPages(index, buildstate->centers, buildstate->dimensions, buildstate->lists, forkNum, &buildstate->listInfo);
|
||||
CreateEntryPages(buildstate, forkNum);
|
||||
|
||||
/* Write WAL for initialization fork since GenericXLog functions do not */
|
||||
if (forkNum == INIT_FORKNUM)
|
||||
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocksInFork(index, forkNum), true);
|
||||
|
||||
FreeBuildState(buildstate);
|
||||
}
|
||||
|
||||
|
||||
@@ -181,14 +181,14 @@ ivfflatvalidate(Oid opclassoid)
|
||||
*
|
||||
* See https://www.postgresql.org/docs/current/index-api.html
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(ivfflathandler);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflathandler);
|
||||
Datum
|
||||
ivfflathandler(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
|
||||
|
||||
amroutine->amstrategies = 0;
|
||||
amroutine->amsupport = 6;
|
||||
amroutine->amsupport = 5;
|
||||
#if PG_VERSION_NUM >= 130000
|
||||
amroutine->amoptsprocnum = 0;
|
||||
#endif
|
||||
|
||||
@@ -28,8 +28,7 @@
|
||||
#define IVFFLAT_NORM_PROC 2
|
||||
#define IVFFLAT_KMEANS_DISTANCE_PROC 3
|
||||
#define IVFFLAT_KMEANS_NORM_PROC 4
|
||||
#define IVFFLAT_NORMALIZE_PROC 5
|
||||
#define IVFFLAT_TYPE_SUPPORT_PROC 6
|
||||
#define IVFFLAT_TYPE_INFO_PROC 5
|
||||
|
||||
#define IVFFLAT_VERSION 1
|
||||
#define IVFFLAT_MAGIC_NUMBER 0x14FF1A7
|
||||
@@ -45,14 +44,6 @@
|
||||
#define IVFFLAT_MAX_LISTS 32768
|
||||
#define IVFFLAT_DEFAULT_PROBES 1
|
||||
|
||||
typedef enum IvfflatType
|
||||
{
|
||||
IVFFLAT_TYPE_VECTOR,
|
||||
IVFFLAT_TYPE_HALFVEC,
|
||||
IVFFLAT_TYPE_BIT,
|
||||
IVFFLAT_TYPE_UNSUPPORTED
|
||||
} IvfflatType;
|
||||
|
||||
/* Build phases */
|
||||
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
|
||||
#define PROGRESS_IVFFLAT_PHASE_KMEANS 2
|
||||
@@ -158,13 +149,22 @@ typedef struct IvfflatLeader
|
||||
char *ivfcenters;
|
||||
} IvfflatLeader;
|
||||
|
||||
typedef struct IvfflatTypeInfo
|
||||
{
|
||||
int maxDimensions;
|
||||
Datum (*normalize) (PG_FUNCTION_ARGS);
|
||||
Size (*itemSize) (int dimensions);
|
||||
void (*updateCenter) (Pointer v, int dimensions, float *x);
|
||||
void (*sumCenter) (Pointer v, float *x);
|
||||
} IvfflatTypeInfo;
|
||||
|
||||
typedef struct IvfflatBuildState
|
||||
{
|
||||
/* Info */
|
||||
Relation heap;
|
||||
Relation index;
|
||||
IndexInfo *indexInfo;
|
||||
IvfflatType type;
|
||||
const IvfflatTypeInfo *typeInfo;
|
||||
|
||||
/* Settings */
|
||||
int dimensions;
|
||||
@@ -178,7 +178,6 @@ typedef struct IvfflatBuildState
|
||||
FmgrInfo *procinfo;
|
||||
FmgrInfo *normprocinfo;
|
||||
FmgrInfo *kmeansnormprocinfo;
|
||||
FmgrInfo *normalizeprocinfo;
|
||||
Oid collation;
|
||||
|
||||
/* Variables */
|
||||
@@ -246,6 +245,7 @@ typedef struct IvfflatScanList
|
||||
|
||||
typedef struct IvfflatScanOpaqueData
|
||||
{
|
||||
const IvfflatTypeInfo *typeInfo;
|
||||
int probes;
|
||||
int dimensions;
|
||||
bool first;
|
||||
@@ -259,7 +259,6 @@ typedef struct IvfflatScanOpaqueData
|
||||
/* Support functions */
|
||||
FmgrInfo *procinfo;
|
||||
FmgrInfo *normprocinfo;
|
||||
FmgrInfo *normalizeprocinfo;
|
||||
Oid collation;
|
||||
Datum (*distfunc) (FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2);
|
||||
|
||||
@@ -271,17 +270,27 @@ typedef struct IvfflatScanOpaqueData
|
||||
typedef IvfflatScanOpaqueData * IvfflatScanOpaque;
|
||||
|
||||
#define VECTOR_ARRAY_SIZE(_length, _size) (sizeof(VectorArrayData) + (_length) * MAXALIGN(_size))
|
||||
#define VECTOR_ARRAY_OFFSET(_arr, _offset) ((char*) (_arr)->items + (_offset) * (_arr)->itemsize)
|
||||
#define VectorArrayGet(_arr, _offset) VECTOR_ARRAY_OFFSET(_arr, _offset)
|
||||
#define VectorArraySet(_arr, _offset, _val) memcpy(VECTOR_ARRAY_OFFSET(_arr, _offset), _val, (_arr)->itemsize)
|
||||
|
||||
/* Use functions instead of macros to avoid double evaluation */
|
||||
|
||||
static inline Pointer
|
||||
VectorArrayGet(VectorArray arr, int offset)
|
||||
{
|
||||
return ((char *) arr->items) + (offset * arr->itemsize);
|
||||
}
|
||||
|
||||
static inline void
|
||||
VectorArraySet(VectorArray arr, int offset, Pointer val)
|
||||
{
|
||||
memcpy(VectorArrayGet(arr, offset), val, VARSIZE_ANY(val));
|
||||
}
|
||||
|
||||
/* Methods */
|
||||
VectorArray VectorArrayInit(int maxlen, int dimensions, Size itemsize);
|
||||
void VectorArrayFree(VectorArray arr);
|
||||
void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatType type);
|
||||
void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers, const IvfflatTypeInfo * typeInfo);
|
||||
FmgrInfo *IvfflatOptionalProcInfo(Relation index, uint16 procnum);
|
||||
IvfflatType IvfflatGetType(Relation index);
|
||||
Datum IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum value);
|
||||
Datum IvfflatNormValue(const IvfflatTypeInfo * typeInfo, Oid collation, Datum value);
|
||||
bool IvfflatCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value);
|
||||
int IvfflatGetLists(Relation index);
|
||||
void IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions);
|
||||
@@ -292,6 +301,7 @@ Buffer IvfflatNewBuffer(Relation index, ForkNumber forkNum);
|
||||
void IvfflatInitPage(Buffer buf, Page page);
|
||||
void IvfflatInitRegisterPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state);
|
||||
void IvfflatInit(void);
|
||||
const IvfflatTypeInfo *IvfflatGetTypeInfo(Relation index);
|
||||
PGDLLEXPORT void IvfflatParallelBuildMain(dsm_segment *seg, shm_toc *toc);
|
||||
|
||||
/* Index access methods */
|
||||
|
||||
@@ -67,6 +67,7 @@ FindInsertPage(Relation index, Datum *values, BlockNumber *insertPage, ListInfo
|
||||
static void
|
||||
InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel)
|
||||
{
|
||||
const IvfflatTypeInfo *typeInfo = IvfflatGetTypeInfo(index);
|
||||
IndexTuple itup;
|
||||
Datum value;
|
||||
FmgrInfo *normprocinfo;
|
||||
@@ -90,9 +91,12 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, R
|
||||
if (!IvfflatCheckNorm(normprocinfo, collation, value))
|
||||
return;
|
||||
|
||||
value = IvfflatNormValue(IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC), collation, value);
|
||||
value = IvfflatNormValue(typeInfo, collation, value);
|
||||
}
|
||||
|
||||
/* Ensure index is valid */
|
||||
IvfflatGetMetaPageInfo(index, NULL, NULL);
|
||||
|
||||
/* Find the insert page - sets the page and list info */
|
||||
FindInsertPage(index, values, &insertPage, &listInfo);
|
||||
Assert(BlockNumberIsValid(insertPage));
|
||||
|
||||
483
src/ivfkmeans.c
483
src/ivfkmeans.c
@@ -89,140 +89,59 @@ InitCenters(Relation index, VectorArray samples, VectorArray centers, float *low
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply norm to vector
|
||||
*/
|
||||
static inline void
|
||||
ApplyNorm(FmgrInfo *normprocinfo, Oid collation, Datum value, IvfflatType type)
|
||||
{
|
||||
double norm = DatumGetFloat8(FunctionCall1Coll(normprocinfo, collation, value));
|
||||
|
||||
/* TODO Handle zero norm */
|
||||
if (norm > 0)
|
||||
{
|
||||
if (type == IVFFLAT_TYPE_VECTOR)
|
||||
{
|
||||
Vector *vec = DatumGetVector(value);
|
||||
|
||||
for (int i = 0; i < vec->dim; i++)
|
||||
vec->x[i] /= norm;
|
||||
}
|
||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
{
|
||||
HalfVector *vec = DatumGetHalfVector(value);
|
||||
|
||||
for (int i = 0; i < vec->dim; i++)
|
||||
vec->x[i] = Float4ToHalfUnchecked(HalfToFloat4(vec->x[i]) / norm);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare vectors
|
||||
*/
|
||||
static int
|
||||
CompareVectors(const void *a, const void *b)
|
||||
{
|
||||
return vector_cmp_internal((Vector *) a, (Vector *) b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare half vectors
|
||||
*/
|
||||
static int
|
||||
CompareHalfVectors(const void *a, const void *b)
|
||||
{
|
||||
return halfvec_cmp_internal((HalfVector *) a, (HalfVector *) b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare bit vectors
|
||||
*/
|
||||
static int
|
||||
CompareBitVectors(const void *a, const void *b)
|
||||
{
|
||||
return DirectFunctionCall2(bitcmp, VarBitPGetDatum((VarBit *) a), VarBitPGetDatum((VarBit *) b));
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick approach if we have little data
|
||||
* Norm centers
|
||||
*/
|
||||
static void
|
||||
QuickCenters(Relation index, VectorArray samples, VectorArray centers, IvfflatType type)
|
||||
NormCenters(const IvfflatTypeInfo * typeInfo, Oid collation, VectorArray centers)
|
||||
{
|
||||
int dimensions = centers->dim;
|
||||
Oid collation = index->rd_indcollation[0];
|
||||
FmgrInfo *normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||
MemoryContext normCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"Ivfflat norm temporary context",
|
||||
ALLOCSET_DEFAULT_SIZES);
|
||||
MemoryContext oldCtx = MemoryContextSwitchTo(normCtx);
|
||||
|
||||
/* Copy existing vectors while avoiding duplicates */
|
||||
if (samples->length > 0)
|
||||
for (int j = 0; j < centers->length; j++)
|
||||
{
|
||||
if (type == IVFFLAT_TYPE_VECTOR)
|
||||
qsort(samples->items, samples->length, samples->itemsize, CompareVectors);
|
||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
qsort(samples->items, samples->length, samples->itemsize, CompareHalfVectors);
|
||||
else if (type == IVFFLAT_TYPE_BIT)
|
||||
qsort(samples->items, samples->length, samples->itemsize, CompareBitVectors);
|
||||
else
|
||||
elog(ERROR, "Unsupported type");
|
||||
Datum center = PointerGetDatum(VectorArrayGet(centers, j));
|
||||
Datum newCenter = IvfflatNormValue(typeInfo, collation, center);
|
||||
Size size = VARSIZE_ANY(DatumGetPointer(newCenter));
|
||||
|
||||
for (int i = 0; i < samples->length; i++)
|
||||
{
|
||||
Datum vec = PointerGetDatum(VectorArrayGet(samples, i));
|
||||
if (size > centers->itemsize)
|
||||
elog(ERROR, "safety check failed");
|
||||
|
||||
if (i == 0 || !datumIsEqual(vec, PointerGetDatum(VectorArrayGet(samples, i - 1)), false, -1))
|
||||
{
|
||||
VectorArraySet(centers, centers->length, DatumGetPointer(vec));
|
||||
centers->length++;
|
||||
}
|
||||
}
|
||||
memcpy(DatumGetPointer(center), DatumGetPointer(newCenter), size);
|
||||
MemoryContextReset(normCtx);
|
||||
}
|
||||
|
||||
/* Fill remaining with random data */
|
||||
MemoryContextSwitchTo(oldCtx);
|
||||
MemoryContextDelete(normCtx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick approach if we have no data
|
||||
*/
|
||||
static void
|
||||
RandomCenters(Relation index, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||
{
|
||||
int dimensions = centers->dim;
|
||||
FmgrInfo *normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||
Oid collation = index->rd_indcollation[0];
|
||||
float *x = (float *) palloc(sizeof(float) * dimensions);
|
||||
|
||||
/* Fill with random data */
|
||||
while (centers->length < centers->maxlen)
|
||||
{
|
||||
Datum center = PointerGetDatum(VectorArrayGet(centers, centers->length));
|
||||
Pointer center = VectorArrayGet(centers, centers->length);
|
||||
|
||||
if (type == IVFFLAT_TYPE_VECTOR)
|
||||
{
|
||||
Vector *vec = DatumGetVector(center);
|
||||
for (int i = 0; i < dimensions; i++)
|
||||
x[i] = (float) RandomDouble();
|
||||
|
||||
SET_VARSIZE(vec, VECTOR_SIZE(dimensions));
|
||||
vec->dim = dimensions;
|
||||
|
||||
for (int j = 0; j < dimensions; j++)
|
||||
vec->x[j] = RandomDouble();
|
||||
}
|
||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
{
|
||||
HalfVector *vec = DatumGetHalfVector(center);
|
||||
|
||||
SET_VARSIZE(vec, HALFVEC_SIZE(dimensions));
|
||||
vec->dim = dimensions;
|
||||
|
||||
for (int j = 0; j < dimensions; j++)
|
||||
vec->x[j] = Float4ToHalfUnchecked((float) RandomDouble());
|
||||
}
|
||||
else if (type == IVFFLAT_TYPE_BIT)
|
||||
{
|
||||
VarBit *vec = DatumGetVarBitP(center);
|
||||
|
||||
SET_VARSIZE(vec, VARBITTOTALLEN(dimensions));
|
||||
VARBITLEN(vec) = dimensions;
|
||||
|
||||
for (int j = 0; j < dimensions; j++)
|
||||
VARBITS(vec)[j / dimensions] |= (RandomDouble() > 0.5 ? 1 : 0) << (7 - (j % 8));
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Unsupported type");
|
||||
|
||||
/* Normalize if needed (only needed for random centers) */
|
||||
if (normprocinfo != NULL)
|
||||
ApplyNorm(normprocinfo, collation, center, type);
|
||||
typeInfo->updateCenter(center, dimensions, x);
|
||||
|
||||
centers->length++;
|
||||
}
|
||||
|
||||
if (normprocinfo != NULL)
|
||||
NormCenters(typeInfo, collation, centers);
|
||||
}
|
||||
|
||||
#ifdef IVFFLAT_MEMORY
|
||||
@@ -242,63 +161,57 @@ ShowMemoryUsage(MemoryContext context, Size estimatedSize)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Sum centers
|
||||
*/
|
||||
static void
|
||||
SumCenters(VectorArray samples, float *agg, int *closestCenters, const IvfflatTypeInfo * typeInfo)
|
||||
{
|
||||
for (int j = 0; j < samples->length; j++)
|
||||
{
|
||||
float *x = agg + ((int64) closestCenters[j] * samples->dim);
|
||||
|
||||
typeInfo->sumCenter(VectorArrayGet(samples, j), x);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update centers
|
||||
*/
|
||||
static void
|
||||
UpdateCenters(float *agg, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||
{
|
||||
for (int j = 0; j < centers->length; j++)
|
||||
{
|
||||
float *x = agg + ((int64) j * centers->dim);
|
||||
|
||||
typeInfo->updateCenter(VectorArrayGet(centers, j), centers->dim, x);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute new centers
|
||||
*/
|
||||
static void
|
||||
ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCenters, int *centerCounts, int *closestCenters, FmgrInfo *normprocinfo, Oid collation, IvfflatType type)
|
||||
ComputeNewCenters(VectorArray samples, float *agg, VectorArray newCenters, int *centerCounts, int *closestCenters, FmgrInfo *normprocinfo, Oid collation, const IvfflatTypeInfo * typeInfo)
|
||||
{
|
||||
int dimensions = aggCenters->dim;
|
||||
int numCenters = aggCenters->maxlen;
|
||||
int dimensions = newCenters->dim;
|
||||
int numCenters = newCenters->length;
|
||||
int numSamples = samples->length;
|
||||
|
||||
/* Reset sum and count */
|
||||
for (int j = 0; j < numCenters; j++)
|
||||
{
|
||||
Vector *vec = (Vector *) VectorArrayGet(aggCenters, j);
|
||||
float *x = agg + ((int64) j * dimensions);
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
vec->x[k] = 0.0;
|
||||
x[k] = 0.0;
|
||||
|
||||
centerCounts[j] = 0;
|
||||
}
|
||||
|
||||
/* Increment sum of closest center */
|
||||
if (type == IVFFLAT_TYPE_VECTOR)
|
||||
{
|
||||
for (int j = 0; j < numSamples; j++)
|
||||
{
|
||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, closestCenters[j]);
|
||||
Vector *vec = (Vector *) VectorArrayGet(samples, j);
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
aggCenter->x[k] += vec->x[k];
|
||||
}
|
||||
}
|
||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
{
|
||||
for (int j = 0; j < numSamples; j++)
|
||||
{
|
||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, closestCenters[j]);
|
||||
HalfVector *vec = (HalfVector *) VectorArrayGet(samples, j);
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
aggCenter->x[k] += HalfToFloat4(vec->x[k]);
|
||||
}
|
||||
}
|
||||
else if (type == IVFFLAT_TYPE_BIT)
|
||||
{
|
||||
for (int j = 0; j < numSamples; j++)
|
||||
{
|
||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, closestCenters[j]);
|
||||
VarBit *vec = (VarBit *) VectorArrayGet(samples, j);
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
aggCenter->x[k] += (float) (((VARBITS(vec)[k / 8]) >> (7 - (k % 8))) & 0x01);
|
||||
}
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Unsupported type");
|
||||
SumCenters(samples, agg, closestCenters, typeInfo);
|
||||
|
||||
/* Increment count of closest center */
|
||||
for (int j = 0; j < numSamples; j++)
|
||||
@@ -307,7 +220,7 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
|
||||
/* Divide sum by count */
|
||||
for (int j = 0; j < numCenters; j++)
|
||||
{
|
||||
Vector *vec = (Vector *) VectorArrayGet(aggCenters, j);
|
||||
float *x = agg + ((int64) j * dimensions);
|
||||
|
||||
if (centerCounts[j] > 0)
|
||||
{
|
||||
@@ -315,59 +228,27 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
|
||||
/* TODO Update bounds */
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
{
|
||||
if (isinf(vec->x[k]))
|
||||
vec->x[k] = vec->x[k] > 0 ? FLT_MAX : -FLT_MAX;
|
||||
if (isinf(x[k]))
|
||||
x[k] = x[k] > 0 ? FLT_MAX : -FLT_MAX;
|
||||
}
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
vec->x[k] /= centerCounts[j];
|
||||
x[k] /= centerCounts[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO Handle empty centers properly */
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
vec->x[k] = RandomDouble();
|
||||
x[k] = RandomDouble();
|
||||
}
|
||||
}
|
||||
|
||||
/* Set new centers if different from agg centers */
|
||||
if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
{
|
||||
for (int j = 0; j < numCenters; j++)
|
||||
{
|
||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, j);
|
||||
HalfVector *newCenter = (HalfVector *) VectorArrayGet(newCenters, j);
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
newCenter->x[k] = Float4ToHalfUnchecked(aggCenter->x[k]);
|
||||
}
|
||||
}
|
||||
else if (type == IVFFLAT_TYPE_BIT)
|
||||
{
|
||||
for (int j = 0; j < numCenters; j++)
|
||||
{
|
||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, j);
|
||||
VarBit *newCenter = (VarBit *) VectorArrayGet(newCenters, j);
|
||||
unsigned char *nx = VARBITS(newCenter);
|
||||
|
||||
for (uint32 k = 0; k < VARBITBYTES(newCenter); k++)
|
||||
nx[k] = 0;
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
nx[k / 8] |= (aggCenter->x[k] > 0.5) << (7 - (k % 8));
|
||||
}
|
||||
}
|
||||
/* Set new centers */
|
||||
UpdateCenters(agg, newCenters, typeInfo);
|
||||
|
||||
/* Normalize if needed */
|
||||
if (normprocinfo != NULL)
|
||||
{
|
||||
for (int j = 0; j < numCenters; j++)
|
||||
{
|
||||
Datum newCenter = PointerGetDatum(VectorArrayGet(newCenters, j));
|
||||
|
||||
ApplyNorm(normprocinfo, collation, newCenter, type);
|
||||
}
|
||||
}
|
||||
NormCenters(typeInfo, collation, newCenters);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -379,7 +260,7 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
|
||||
* https://www.aaai.org/Papers/ICML/2003/ICML03-022.pdf
|
||||
*/
|
||||
static void
|
||||
ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatType type)
|
||||
ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||
{
|
||||
FmgrInfo *procinfo;
|
||||
FmgrInfo *normprocinfo;
|
||||
@@ -388,7 +269,7 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
||||
int numCenters = centers->maxlen;
|
||||
int numSamples = samples->length;
|
||||
VectorArray newCenters;
|
||||
VectorArray aggCenters;
|
||||
float *agg;
|
||||
int *centerCounts;
|
||||
int *closestCenters;
|
||||
float *lowerBound;
|
||||
@@ -396,14 +277,12 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
||||
float *s;
|
||||
float *halfcdist;
|
||||
float *newcdist;
|
||||
MemoryContext kmeansCtx;
|
||||
MemoryContext oldCtx;
|
||||
|
||||
/* Calculate allocation sizes */
|
||||
Size samplesSize = VECTOR_ARRAY_SIZE(samples->maxlen, samples->itemsize);
|
||||
Size centersSize = VECTOR_ARRAY_SIZE(centers->maxlen, centers->itemsize);
|
||||
Size newCentersSize = VECTOR_ARRAY_SIZE(numCenters, centers->itemsize);
|
||||
Size aggCentersSize = type == IVFFLAT_TYPE_VECTOR ? 0 : VECTOR_ARRAY_SIZE(numCenters, VECTOR_SIZE(dimensions));
|
||||
Size aggSize = sizeof(float) * (int64) numCenters * dimensions;
|
||||
Size centerCountsSize = sizeof(int) * numCenters;
|
||||
Size closestCentersSize = sizeof(int) * numSamples;
|
||||
Size lowerBoundSize = sizeof(float) * numSamples * numCenters;
|
||||
@@ -413,7 +292,7 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
||||
Size newcdistSize = sizeof(float) * numCenters;
|
||||
|
||||
/* Calculate total size */
|
||||
Size totalSize = samplesSize + centersSize + newCentersSize + aggCentersSize + centerCountsSize + closestCentersSize + lowerBoundSize + upperBoundSize + sSize + halfcdistSize + newcdistSize;
|
||||
Size totalSize = samplesSize + centersSize + newCentersSize + aggSize + centerCountsSize + closestCentersSize + lowerBoundSize + upperBoundSize + sSize + halfcdistSize + newcdistSize;
|
||||
|
||||
/* Check memory requirements */
|
||||
/* Add one to error message to ceil */
|
||||
@@ -432,14 +311,9 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
||||
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||
collation = index->rd_indcollation[0];
|
||||
|
||||
/* Use memory context */
|
||||
kmeansCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"Ivfflat kmeans temporary context",
|
||||
ALLOCSET_DEFAULT_SIZES);
|
||||
oldCtx = MemoryContextSwitchTo(kmeansCtx);
|
||||
|
||||
/* Allocate space */
|
||||
/* Use float instead of double to save memory */
|
||||
agg = palloc(aggSize);
|
||||
centerCounts = palloc(centerCountsSize);
|
||||
closestCenters = palloc(closestCentersSize);
|
||||
lowerBound = palloc_extended(lowerBoundSize, MCXT_ALLOC_HUGE);
|
||||
@@ -448,49 +322,12 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
||||
halfcdist = palloc_extended(halfcdistSize, MCXT_ALLOC_HUGE);
|
||||
newcdist = palloc(newcdistSize);
|
||||
|
||||
aggCenters = VectorArrayInit(numCenters, dimensions, VECTOR_SIZE(dimensions));
|
||||
for (int j = 0; j < numCenters; j++)
|
||||
{
|
||||
Vector *vec = (Vector *) VectorArrayGet(aggCenters, j);
|
||||
|
||||
SET_VARSIZE(vec, VECTOR_SIZE(dimensions));
|
||||
vec->dim = dimensions;
|
||||
}
|
||||
|
||||
if (type == IVFFLAT_TYPE_VECTOR)
|
||||
{
|
||||
/* Use same centers to save memory */
|
||||
newCenters = aggCenters;
|
||||
}
|
||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
{
|
||||
newCenters = VectorArrayInit(numCenters, dimensions, centers->itemsize);
|
||||
|
||||
for (int j = 0; j < numCenters; j++)
|
||||
{
|
||||
HalfVector *vec = (HalfVector *) VectorArrayGet(newCenters, j);
|
||||
|
||||
SET_VARSIZE(vec, HALFVEC_SIZE(dimensions));
|
||||
vec->dim = dimensions;
|
||||
}
|
||||
}
|
||||
else if (type == IVFFLAT_TYPE_BIT)
|
||||
{
|
||||
newCenters = VectorArrayInit(numCenters, dimensions, centers->itemsize);
|
||||
|
||||
for (int j = 0; j < numCenters; j++)
|
||||
{
|
||||
VarBit *vec = (VarBit *) VectorArrayGet(newCenters, j);
|
||||
|
||||
SET_VARSIZE(vec, VARBITTOTALLEN(dimensions));
|
||||
VARBITLEN(vec) = dimensions;
|
||||
}
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Unsupported type");
|
||||
/* Initialize new centers */
|
||||
newCenters = VectorArrayInit(numCenters, dimensions, centers->itemsize);
|
||||
newCenters->length = numCenters;
|
||||
|
||||
#ifdef IVFFLAT_MEMORY
|
||||
ShowMemoryUsage(oldCtx, totalSize);
|
||||
ShowMemoryUsage(MemoryContextGetParent(CurrentMemoryContext));
|
||||
#endif
|
||||
|
||||
/* Pick initial centers */
|
||||
@@ -627,7 +464,7 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
||||
}
|
||||
|
||||
/* Step 4: For each center c, let m(c) be mean of all points assigned */
|
||||
ComputeNewCenters(samples, aggCenters, newCenters, centerCounts, closestCenters, normprocinfo, collation, type);
|
||||
ComputeNewCenters(samples, agg, newCenters, centerCounts, closestCenters, normprocinfo, collation, typeInfo);
|
||||
|
||||
/* Step 5 */
|
||||
for (int j = 0; j < numCenters; j++)
|
||||
@@ -658,88 +495,68 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
||||
if (changes == 0 && iteration != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(oldCtx);
|
||||
MemoryContextDelete(kmeansCtx);
|
||||
/*
|
||||
* Ensure no NaN or infinite values
|
||||
*/
|
||||
static void
|
||||
CheckElements(VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||
{
|
||||
float *scratch = palloc(sizeof(float) * centers->dim);
|
||||
|
||||
for (int i = 0; i < centers->length; i++)
|
||||
{
|
||||
for (int j = 0; j < centers->dim; j++)
|
||||
scratch[j] = 0;
|
||||
|
||||
/* /fp:fast may not propagate NaN with MSVC, but that's alright */
|
||||
typeInfo->sumCenter(VectorArrayGet(centers, i), scratch);
|
||||
|
||||
for (int j = 0; j < centers->dim; j++)
|
||||
{
|
||||
if (isnan(scratch[j]))
|
||||
elog(ERROR, "NaN detected. Please report a bug.");
|
||||
|
||||
if (isinf(scratch[j]))
|
||||
elog(ERROR, "Infinite value detected. Please report a bug.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure no zero vectors for cosine distance
|
||||
*/
|
||||
static void
|
||||
CheckNorms(VectorArray centers, Relation index)
|
||||
{
|
||||
/* Check NORM_PROC instead of KMEANS_NORM_PROC */
|
||||
FmgrInfo *normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
||||
Oid collation = index->rd_indcollation[0];
|
||||
|
||||
if (normprocinfo == NULL)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < centers->length; i++)
|
||||
{
|
||||
double norm = DatumGetFloat8(FunctionCall1Coll(normprocinfo, collation, PointerGetDatum(VectorArrayGet(centers, i))));
|
||||
|
||||
if (norm == 0)
|
||||
elog(ERROR, "Zero norm detected. Please report a bug.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect issues with centers
|
||||
*/
|
||||
static void
|
||||
CheckCenters(Relation index, VectorArray centers, IvfflatType type)
|
||||
CheckCenters(Relation index, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||
{
|
||||
FmgrInfo *normprocinfo;
|
||||
|
||||
if (centers->length != centers->maxlen)
|
||||
elog(ERROR, "Not enough centers. Please report a bug.");
|
||||
|
||||
/* Ensure no NaN or infinite values */
|
||||
for (int i = 0; i < centers->length; i++)
|
||||
{
|
||||
if (type == IVFFLAT_TYPE_VECTOR)
|
||||
{
|
||||
Vector *vec = (Vector *) VectorArrayGet(centers, i);
|
||||
|
||||
for (int j = 0; j < vec->dim; j++)
|
||||
{
|
||||
if (isnan(vec->x[j]))
|
||||
elog(ERROR, "NaN detected. Please report a bug.");
|
||||
|
||||
if (isinf(vec->x[j]))
|
||||
elog(ERROR, "Infinite value detected. Please report a bug.");
|
||||
}
|
||||
}
|
||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
{
|
||||
HalfVector *vec = (HalfVector *) VectorArrayGet(centers, i);
|
||||
|
||||
for (int j = 0; j < vec->dim; j++)
|
||||
{
|
||||
if (HalfIsNan(vec->x[j]))
|
||||
elog(ERROR, "NaN detected. Please report a bug.");
|
||||
|
||||
if (HalfIsInf(vec->x[j]))
|
||||
elog(ERROR, "Infinite value detected. Please report a bug.");
|
||||
}
|
||||
}
|
||||
else if (type != IVFFLAT_TYPE_BIT)
|
||||
elog(ERROR, "Unsupported type");
|
||||
}
|
||||
|
||||
if (type != IVFFLAT_TYPE_BIT)
|
||||
{
|
||||
/* Ensure no duplicate centers */
|
||||
/* Fine to sort in-place */
|
||||
if (type == IVFFLAT_TYPE_VECTOR)
|
||||
qsort(centers->items, centers->length, centers->itemsize, CompareVectors);
|
||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
||||
qsort(centers->items, centers->length, centers->itemsize, CompareHalfVectors);
|
||||
else
|
||||
elog(ERROR, "Unsupported type");
|
||||
|
||||
for (int i = 1; i < centers->length; i++)
|
||||
{
|
||||
if (datumIsEqual(PointerGetDatum(VectorArrayGet(centers, i)), PointerGetDatum(VectorArrayGet(centers, i - 1)), false, -1))
|
||||
elog(ERROR, "Duplicate centers detected. Please report a bug.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure no zero vectors for cosine distance */
|
||||
/* Check NORM_PROC instead of KMEANS_NORM_PROC */
|
||||
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
||||
if (normprocinfo != NULL)
|
||||
{
|
||||
Oid collation = index->rd_indcollation[0];
|
||||
|
||||
for (int i = 0; i < centers->length; i++)
|
||||
{
|
||||
double norm = DatumGetFloat8(FunctionCall1Coll(normprocinfo, collation, PointerGetDatum(VectorArrayGet(centers, i))));
|
||||
|
||||
if (norm == 0)
|
||||
elog(ERROR, "Zero norm detected. Please report a bug.");
|
||||
}
|
||||
}
|
||||
CheckElements(centers, typeInfo);
|
||||
CheckNorms(centers, index);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -747,12 +564,20 @@ CheckCenters(Relation index, VectorArray centers, IvfflatType type)
|
||||
* We use spherical k-means for inner product and cosine
|
||||
*/
|
||||
void
|
||||
IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatType type)
|
||||
IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||
{
|
||||
if (samples->length <= centers->maxlen)
|
||||
QuickCenters(index, samples, centers, type);
|
||||
else
|
||||
ElkanKmeans(index, samples, centers, type);
|
||||
MemoryContext kmeansCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"Ivfflat kmeans temporary context",
|
||||
ALLOCSET_DEFAULT_SIZES);
|
||||
MemoryContext oldCtx = MemoryContextSwitchTo(kmeansCtx);
|
||||
|
||||
CheckCenters(index, centers, type);
|
||||
if (samples->length == 0)
|
||||
RandomCenters(index, centers, typeInfo);
|
||||
else
|
||||
ElkanKmeans(index, samples, centers, typeInfo);
|
||||
|
||||
CheckCenters(index, centers, typeInfo);
|
||||
|
||||
MemoryContextSwitchTo(oldCtx);
|
||||
MemoryContextDelete(kmeansCtx);
|
||||
}
|
||||
|
||||
@@ -209,9 +209,9 @@ GetScanValue(IndexScanDesc scan)
|
||||
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
|
||||
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
|
||||
|
||||
/* Check normprocinfo since normalizeprocinfo not set for vector */
|
||||
/* Normalize if needed */
|
||||
if (so->normprocinfo != NULL)
|
||||
value = IvfflatNormValue(so->normalizeprocinfo, so->collation, value);
|
||||
value = IvfflatNormValue(so->typeInfo, so->collation, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -242,6 +242,7 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
|
||||
probes = lists;
|
||||
|
||||
so = (IvfflatScanOpaque) palloc(offsetof(IvfflatScanOpaqueData, lists) + probes * sizeof(IvfflatScanList));
|
||||
so->typeInfo = IvfflatGetTypeInfo(index);
|
||||
so->first = true;
|
||||
so->probes = probes;
|
||||
so->dimensions = dimensions;
|
||||
@@ -249,7 +250,6 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
|
||||
/* Set support functions */
|
||||
so->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC);
|
||||
so->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
||||
so->normalizeprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC);
|
||||
so->collation = index->rd_indcollation[0];
|
||||
|
||||
/* Create tuple description for sorting */
|
||||
|
||||
187
src/ivfutils.c
187
src/ivfutils.c
@@ -1,8 +1,11 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/generic_xlog.h"
|
||||
#include "bitvec.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "fmgr.h"
|
||||
#include "halfutils.h"
|
||||
#include "halfvec.h"
|
||||
#include "ivfflat.h"
|
||||
#include "storage/bufmgr.h"
|
||||
|
||||
@@ -61,37 +64,13 @@ IvfflatOptionalProcInfo(Relation index, uint16 procnum)
|
||||
return index_getprocinfo(index, 1, procnum);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get type
|
||||
*/
|
||||
IvfflatType
|
||||
IvfflatGetType(Relation index)
|
||||
{
|
||||
FmgrInfo *procinfo = IvfflatOptionalProcInfo(index, IVFFLAT_TYPE_SUPPORT_PROC);
|
||||
Oid typid = TupleDescAttr(index->rd_att, 0)->atttypid;
|
||||
IvfflatType result;
|
||||
|
||||
if (procinfo == NULL)
|
||||
return IVFFLAT_TYPE_VECTOR;
|
||||
|
||||
result = (IvfflatType) DatumGetInt32(FunctionCall1(procinfo, ObjectIdGetDatum(typid)));
|
||||
|
||||
if (result == IVFFLAT_TYPE_UNSUPPORTED)
|
||||
elog(ERROR, "type not supported for ivfflat index");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalize value
|
||||
*/
|
||||
Datum
|
||||
IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum value)
|
||||
IvfflatNormValue(const IvfflatTypeInfo * typeInfo, Oid collation, Datum value)
|
||||
{
|
||||
if (procinfo == NULL)
|
||||
return DirectFunctionCall1(l2_normalize, value);
|
||||
|
||||
return FunctionCall1Coll(procinfo, collation, value);
|
||||
return DirectFunctionCall1Coll(typeInfo->normalize, collation, value);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -191,7 +170,11 @@ IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions)
|
||||
page = BufferGetPage(buf);
|
||||
metap = IvfflatPageGetMeta(page);
|
||||
|
||||
*lists = metap->lists;
|
||||
if (unlikely(metap->magicNumber != IVFFLAT_MAGIC_NUMBER))
|
||||
elog(ERROR, "ivfflat index is not valid");
|
||||
|
||||
if (lists != NULL)
|
||||
*lists = metap->lists;
|
||||
|
||||
if (dimensions != NULL)
|
||||
*dimensions = metap->dimensions;
|
||||
@@ -246,21 +229,145 @@ IvfflatUpdateList(Relation index, ListInfo listInfo,
|
||||
}
|
||||
}
|
||||
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ivfflat_support);
|
||||
Datum
|
||||
halfvec_ivfflat_support(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_INT32(IVFFLAT_TYPE_HALFVEC);
|
||||
};
|
||||
PGDLLEXPORT Datum l2_normalize(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum halfvec_l2_normalize(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum sparsevec_l2_normalize(PG_FUNCTION_ARGS);
|
||||
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(bit_ivfflat_support);
|
||||
Datum
|
||||
bit_ivfflat_support(PG_FUNCTION_ARGS)
|
||||
static Size
|
||||
VectorItemSize(int dimensions)
|
||||
{
|
||||
Oid typid = PG_GETARG_OID(0);
|
||||
return VECTOR_SIZE(dimensions);
|
||||
}
|
||||
|
||||
if (typid == BITOID)
|
||||
PG_RETURN_INT32(IVFFLAT_TYPE_BIT);
|
||||
static Size
|
||||
HalfvecItemSize(int dimensions)
|
||||
{
|
||||
return HALFVEC_SIZE(dimensions);
|
||||
}
|
||||
|
||||
static Size
|
||||
BitItemSize(int dimensions)
|
||||
{
|
||||
return VARBITTOTALLEN(dimensions);
|
||||
}
|
||||
|
||||
static void
|
||||
VectorUpdateCenter(Pointer v, int dimensions, float *x)
|
||||
{
|
||||
Vector *vec = (Vector *) v;
|
||||
|
||||
SET_VARSIZE(vec, VECTOR_SIZE(dimensions));
|
||||
vec->dim = dimensions;
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
vec->x[k] = x[k];
|
||||
}
|
||||
|
||||
static void
|
||||
HalfvecUpdateCenter(Pointer v, int dimensions, float *x)
|
||||
{
|
||||
HalfVector *vec = (HalfVector *) v;
|
||||
|
||||
SET_VARSIZE(vec, HALFVEC_SIZE(dimensions));
|
||||
vec->dim = dimensions;
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
vec->x[k] = Float4ToHalfUnchecked(x[k]);
|
||||
}
|
||||
|
||||
static void
|
||||
BitUpdateCenter(Pointer v, int dimensions, float *x)
|
||||
{
|
||||
VarBit *vec = (VarBit *) v;
|
||||
unsigned char *nx = VARBITS(vec);
|
||||
|
||||
SET_VARSIZE(vec, VARBITTOTALLEN(dimensions));
|
||||
VARBITLEN(vec) = dimensions;
|
||||
|
||||
for (uint32 k = 0; k < VARBITBYTES(vec); k++)
|
||||
nx[k] = 0;
|
||||
|
||||
for (int k = 0; k < dimensions; k++)
|
||||
nx[k / 8] |= (x[k] > 0.5 ? 1 : 0) << (7 - (k % 8));
|
||||
}
|
||||
|
||||
static void
|
||||
VectorSumCenter(Pointer v, float *x)
|
||||
{
|
||||
Vector *vec = (Vector *) v;
|
||||
|
||||
for (int k = 0; k < vec->dim; k++)
|
||||
x[k] += vec->x[k];
|
||||
}
|
||||
|
||||
static void
|
||||
HalfvecSumCenter(Pointer v, float *x)
|
||||
{
|
||||
HalfVector *vec = (HalfVector *) v;
|
||||
|
||||
for (int k = 0; k < vec->dim; k++)
|
||||
x[k] += HalfToFloat4(vec->x[k]);
|
||||
}
|
||||
|
||||
static void
|
||||
BitSumCenter(Pointer v, float *x)
|
||||
{
|
||||
VarBit *vec = (VarBit *) v;
|
||||
|
||||
for (int k = 0; k < VARBITLEN(vec); k++)
|
||||
x[k] += (float) (((VARBITS(vec)[k / 8]) >> (7 - (k % 8))) & 0x01);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get type info
|
||||
*/
|
||||
const IvfflatTypeInfo *
|
||||
IvfflatGetTypeInfo(Relation index)
|
||||
{
|
||||
FmgrInfo *procinfo = IvfflatOptionalProcInfo(index, IVFFLAT_TYPE_INFO_PROC);
|
||||
|
||||
if (procinfo == NULL)
|
||||
{
|
||||
static const IvfflatTypeInfo typeInfo = {
|
||||
.maxDimensions = IVFFLAT_MAX_DIM,
|
||||
.normalize = l2_normalize,
|
||||
.itemSize = VectorItemSize,
|
||||
.updateCenter = VectorUpdateCenter,
|
||||
.sumCenter = VectorSumCenter
|
||||
};
|
||||
|
||||
return (&typeInfo);
|
||||
}
|
||||
else
|
||||
PG_RETURN_INT32(IVFFLAT_TYPE_UNSUPPORTED);
|
||||
return (const IvfflatTypeInfo *) DatumGetPointer(FunctionCall0Coll(procinfo, InvalidOid));
|
||||
}
|
||||
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflat_halfvec_support);
|
||||
Datum
|
||||
ivfflat_halfvec_support(PG_FUNCTION_ARGS)
|
||||
{
|
||||
static const IvfflatTypeInfo typeInfo = {
|
||||
.maxDimensions = IVFFLAT_MAX_DIM * 2,
|
||||
.normalize = halfvec_l2_normalize,
|
||||
.itemSize = HalfvecItemSize,
|
||||
.updateCenter = HalfvecUpdateCenter,
|
||||
.sumCenter = HalfvecSumCenter
|
||||
};
|
||||
|
||||
PG_RETURN_POINTER(&typeInfo);
|
||||
};
|
||||
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflat_bit_support);
|
||||
Datum
|
||||
ivfflat_bit_support(PG_FUNCTION_ARGS)
|
||||
{
|
||||
static const IvfflatTypeInfo typeInfo = {
|
||||
.maxDimensions = IVFFLAT_MAX_DIM * 32,
|
||||
.normalize = NULL,
|
||||
.itemSize = BitItemSize,
|
||||
.updateCenter = BitUpdateCenter,
|
||||
.sumCenter = BitSumCenter
|
||||
};
|
||||
|
||||
PG_RETURN_POINTER(&typeInfo);
|
||||
};
|
||||
|
||||
116
src/sparsevec.c
116
src/sparsevec.c
@@ -3,6 +3,7 @@
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "common/string.h"
|
||||
#include "fmgr.h"
|
||||
#include "halfutils.h"
|
||||
#include "halfvec.h"
|
||||
@@ -97,27 +98,24 @@ CheckIndex(int32 *indices, int i, int dim)
|
||||
{
|
||||
int32 index = indices[i];
|
||||
|
||||
if (index < 1)
|
||||
if (index < 0 || index >= dim)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||
errmsg("index must be greater than zero")));
|
||||
|
||||
if (index > dim)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||
errmsg("index must be less than or equal to dimensions")));
|
||||
errmsg("sparsevec index out of bounds")));
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
if (index < indices[i - 1])
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||
errmsg("indexes must be in ascending order")));
|
||||
errmsg("sparsevec indices must be in ascending order")));
|
||||
|
||||
if (index == indices[i - 1])
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||
errmsg("indexes must not contain duplicates")));
|
||||
errmsg("sparsevec indices must not contain duplicates")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,13 +188,13 @@ CompareIndices(const void *a, const void *b)
|
||||
/*
|
||||
* Convert textual representation to internal representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_in);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_in);
|
||||
Datum
|
||||
sparsevec_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *lit = PG_GETARG_CSTRING(0);
|
||||
int32 typmod = PG_GETARG_INT32(2);
|
||||
int dim;
|
||||
long dim;
|
||||
char *pt = lit;
|
||||
char *stringEnd;
|
||||
SparseVector *result;
|
||||
@@ -246,7 +244,6 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
||||
long index;
|
||||
float value;
|
||||
|
||||
/* TODO Better error */
|
||||
if (nnz == maxNnz)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
@@ -262,7 +259,6 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
||||
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
|
||||
|
||||
/* Use similar logic as int2vectorin */
|
||||
errno = 0;
|
||||
index = strtol(pt, &stringEnd, 10);
|
||||
|
||||
if (stringEnd == pt)
|
||||
@@ -270,10 +266,11 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
|
||||
|
||||
if (errno == ERANGE || index < 1 || index > INT_MAX)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("index \"%ld\" is out of range for type sparsevec", index)));
|
||||
/* Keep in int range for correct error message later */
|
||||
if (index > INT_MAX)
|
||||
index = INT_MAX;
|
||||
else if (index < INT_MIN + 1)
|
||||
index = INT_MIN + 1;
|
||||
|
||||
pt = stringEnd;
|
||||
|
||||
@@ -312,7 +309,8 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
||||
/* Do not store zero values */
|
||||
if (value != 0)
|
||||
{
|
||||
elements[nnz].index = index;
|
||||
/* Convert 1-based numbering (SQL) to 0-based (C) */
|
||||
elements[nnz].index = index - 1;
|
||||
elements[nnz].value = value;
|
||||
nnz++;
|
||||
}
|
||||
@@ -351,7 +349,6 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
||||
pt++;
|
||||
|
||||
/* Use similar logic as int2vectorin */
|
||||
errno = 0;
|
||||
dim = strtol(pt, &stringEnd, 10);
|
||||
|
||||
if (stringEnd == pt)
|
||||
@@ -359,6 +356,12 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
|
||||
|
||||
/* Keep in int range for correct error message later */
|
||||
if (dim > INT_MAX)
|
||||
dim = INT_MAX;
|
||||
else if (dim < INT_MIN)
|
||||
dim = INT_MIN;
|
||||
|
||||
pt = stringEnd;
|
||||
|
||||
/* Only whitespace is allowed after the closing brace */
|
||||
@@ -406,7 +409,7 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert internal representation to textual representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_out);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_out);
|
||||
Datum
|
||||
sparsevec_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -441,7 +444,8 @@ sparsevec_out(PG_FUNCTION_ARGS)
|
||||
if (i > 0)
|
||||
AppendChar(ptr, ',');
|
||||
|
||||
AppendInt(ptr, sparsevec->indices[i]);
|
||||
/* Convert 0-based numbering (C) to 1-based (SQL) */
|
||||
AppendInt(ptr, sparsevec->indices[i] + 1);
|
||||
AppendChar(ptr, ':');
|
||||
AppendFloat(ptr, values[i]);
|
||||
}
|
||||
@@ -458,7 +462,7 @@ sparsevec_out(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert type modifier
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_typmod_in);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_typmod_in);
|
||||
Datum
|
||||
sparsevec_typmod_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -489,7 +493,7 @@ sparsevec_typmod_in(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert external binary representation to internal representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_recv);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_recv);
|
||||
Datum
|
||||
sparsevec_recv(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -517,6 +521,7 @@ sparsevec_recv(PG_FUNCTION_ARGS)
|
||||
result = InitSparseVector(dim, nnz);
|
||||
values = SPARSEVEC_VALUES(result);
|
||||
|
||||
/* Binary representation uses zero-based numbering for indices */
|
||||
for (int i = 0; i < nnz; i++)
|
||||
{
|
||||
result->indices[i] = pq_getmsgint(buf, sizeof(int32));
|
||||
@@ -527,6 +532,7 @@ sparsevec_recv(PG_FUNCTION_ARGS)
|
||||
{
|
||||
values[i] = pq_getmsgfloat4(buf);
|
||||
CheckElement(values[i]);
|
||||
|
||||
if (values[i] == 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||
@@ -539,7 +545,7 @@ sparsevec_recv(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert internal representation to the external binary representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_send);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_send);
|
||||
Datum
|
||||
sparsevec_send(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -551,8 +557,11 @@ sparsevec_send(PG_FUNCTION_ARGS)
|
||||
pq_sendint(&buf, svec->dim, sizeof(int32));
|
||||
pq_sendint(&buf, svec->nnz, sizeof(int32));
|
||||
pq_sendint(&buf, svec->unused, sizeof(int32));
|
||||
|
||||
/* Binary representation uses zero-based numbering for indices */
|
||||
for (int i = 0; i < svec->nnz; i++)
|
||||
pq_sendint(&buf, svec->indices[i], sizeof(int32));
|
||||
|
||||
for (int i = 0; i < svec->nnz; i++)
|
||||
pq_sendfloat4(&buf, values[i]);
|
||||
|
||||
@@ -563,7 +572,7 @@ sparsevec_send(PG_FUNCTION_ARGS)
|
||||
* Convert sparse vector to sparse vector
|
||||
* This is needed to check the type modifier
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec);
|
||||
Datum
|
||||
sparsevec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -578,7 +587,7 @@ sparsevec(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert dense vector to sparse vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_sparsevec);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_to_sparsevec);
|
||||
Datum
|
||||
vector_to_sparsevec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -609,7 +618,7 @@ vector_to_sparsevec(PG_FUNCTION_ARGS)
|
||||
if (j >= result->nnz)
|
||||
elog(ERROR, "safety check failed");
|
||||
|
||||
result->indices[j] = i + 1;
|
||||
result->indices[j] = i;
|
||||
values[j] = vec->x[i];
|
||||
j++;
|
||||
}
|
||||
@@ -621,7 +630,7 @@ vector_to_sparsevec(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert half vector to sparse vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_sparsevec);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_to_sparsevec);
|
||||
Datum
|
||||
halfvec_to_sparsevec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -652,7 +661,7 @@ halfvec_to_sparsevec(PG_FUNCTION_ARGS)
|
||||
if (j >= result->nnz)
|
||||
elog(ERROR, "safety check failed");
|
||||
|
||||
result->indices[j] = i + 1;
|
||||
result->indices[j] = i;
|
||||
values[j] = HalfToFloat4(vec->x[i]);
|
||||
j++;
|
||||
}
|
||||
@@ -683,7 +692,7 @@ SparsevecL2SquaredDistance(SparseVector * a, SparseVector * b)
|
||||
|
||||
if (ai == bi)
|
||||
{
|
||||
double diff = ax[i] - bx[j];
|
||||
float diff = ax[i] - bx[j];
|
||||
|
||||
distance += diff * diff;
|
||||
}
|
||||
@@ -712,7 +721,7 @@ SparsevecL2SquaredDistance(SparseVector * a, SparseVector * b)
|
||||
/*
|
||||
* Get the L2 distance between sparse vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_distance);
|
||||
Datum
|
||||
sparsevec_l2_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -721,14 +730,14 @@ sparsevec_l2_distance(PG_FUNCTION_ARGS)
|
||||
|
||||
CheckDims(a, b);
|
||||
|
||||
PG_RETURN_FLOAT8(sqrt(SparsevecL2SquaredDistance(a, b)));
|
||||
PG_RETURN_FLOAT8(sqrt((double) SparsevecL2SquaredDistance(a, b)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the L2 squared distance between sparse vectors
|
||||
* This saves a sqrt calculation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_squared_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_squared_distance);
|
||||
Datum
|
||||
sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -737,7 +746,7 @@ sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||
|
||||
CheckDims(a, b);
|
||||
|
||||
PG_RETURN_FLOAT8(SparsevecL2SquaredDistance(a, b));
|
||||
PG_RETURN_FLOAT8((double) SparsevecL2SquaredDistance(a, b));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -779,7 +788,7 @@ SparsevecInnerProduct(SparseVector * a, SparseVector * b)
|
||||
/*
|
||||
* Get the inner product of two sparse vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_inner_product);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_inner_product);
|
||||
Datum
|
||||
sparsevec_inner_product(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -788,13 +797,13 @@ sparsevec_inner_product(PG_FUNCTION_ARGS)
|
||||
|
||||
CheckDims(a, b);
|
||||
|
||||
PG_RETURN_FLOAT8(SparsevecInnerProduct(a, b));
|
||||
PG_RETURN_FLOAT8((double) SparsevecInnerProduct(a, b));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the negative inner product of two sparse vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_negative_inner_product);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_negative_inner_product);
|
||||
Datum
|
||||
sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -803,13 +812,13 @@ sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
|
||||
|
||||
CheckDims(a, b);
|
||||
|
||||
PG_RETURN_FLOAT8(-SparsevecInnerProduct(a, b));
|
||||
PG_RETURN_FLOAT8((double) -SparsevecInnerProduct(a, b));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the cosine distance between two sparse vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_cosine_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_cosine_distance);
|
||||
Datum
|
||||
sparsevec_cosine_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -854,7 +863,7 @@ sparsevec_cosine_distance(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the L1 distance between two sparse vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l1_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l1_distance);
|
||||
Datum
|
||||
sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -862,7 +871,7 @@ sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
||||
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
|
||||
float *ax = SPARSEVEC_VALUES(a);
|
||||
float *bx = SPARSEVEC_VALUES(b);
|
||||
double distance = 0.0;
|
||||
float distance = 0.0;
|
||||
int bpos = 0;
|
||||
|
||||
CheckDims(a, b);
|
||||
@@ -897,13 +906,13 @@ sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
||||
for (int j = bpos; j < b->nnz; j++)
|
||||
distance += fabsf(bx[j]);
|
||||
|
||||
PG_RETURN_FLOAT8(distance);
|
||||
PG_RETURN_FLOAT8((double) distance);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the L2 norm of a sparse vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_norm);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_norm);
|
||||
Datum
|
||||
sparsevec_l2_norm(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -921,7 +930,7 @@ sparsevec_l2_norm(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Normalize a sparse vector with the L2 norm
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_normalize);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_normalize);
|
||||
Datum
|
||||
sparsevec_l2_normalize(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1013,11 +1022,10 @@ sparsevec_cmp_internal(SparseVector * a, SparseVector * b)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check <= dim since indices start at 1 */
|
||||
if (a->nnz < b->nnz && b->indices[nnz] <= a->dim)
|
||||
if (a->nnz < b->nnz && b->indices[nnz] < a->dim)
|
||||
return bx[nnz] < 0 ? 1 : -1;
|
||||
|
||||
if (a->nnz > b->nnz && a->indices[nnz] <= b->dim)
|
||||
if (a->nnz > b->nnz && a->indices[nnz] < b->dim)
|
||||
return ax[nnz] < 0 ? -1 : 1;
|
||||
|
||||
if (a->dim < b->dim)
|
||||
@@ -1032,7 +1040,7 @@ sparsevec_cmp_internal(SparseVector * a, SparseVector * b)
|
||||
/*
|
||||
* Less than
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_lt);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_lt);
|
||||
Datum
|
||||
sparsevec_lt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1045,7 +1053,7 @@ sparsevec_lt(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Less than or equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_le);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_le);
|
||||
Datum
|
||||
sparsevec_le(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1058,7 +1066,7 @@ sparsevec_le(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_eq);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_eq);
|
||||
Datum
|
||||
sparsevec_eq(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1071,7 +1079,7 @@ sparsevec_eq(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Not equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_ne);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_ne);
|
||||
Datum
|
||||
sparsevec_ne(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1084,7 +1092,7 @@ sparsevec_ne(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Greater than or equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_ge);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_ge);
|
||||
Datum
|
||||
sparsevec_ge(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1097,7 +1105,7 @@ sparsevec_ge(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Greater than
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_gt);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_gt);
|
||||
Datum
|
||||
sparsevec_gt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1110,7 +1118,7 @@ sparsevec_gt(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Compare sparse vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_cmp);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_cmp);
|
||||
Datum
|
||||
sparsevec_cmp(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
#ifndef SPARSEVEC_H
|
||||
#define SPARSEVEC_H
|
||||
|
||||
#include "fmgr.h"
|
||||
|
||||
#define SPARSEVEC_MAX_DIM 100000
|
||||
#define SPARSEVEC_MAX_DIM 1000000000
|
||||
#define SPARSEVEC_MAX_NNZ 16000
|
||||
|
||||
/* Ensure values are aligned */
|
||||
#define SPARSEVEC_SIZE(_nnz) (offsetof(SparseVector, indices) + MAXALIGN((_nnz) * sizeof(int32)) + (_nnz * sizeof(float)))
|
||||
#define SPARSEVEC_VALUES(x) ((float *) (((char *) (x)) + offsetof(SparseVector, indices) + MAXALIGN((x)->nnz * sizeof(int32))))
|
||||
#define DatumGetSparseVector(x) ((SparseVector *) PG_DETOAST_DATUM(x))
|
||||
#define PG_GETARG_SPARSEVEC_P(x) DatumGetSparseVector(PG_GETARG_DATUM(x))
|
||||
#define PG_RETURN_SPARSEVEC_P(x) PG_RETURN_POINTER(x)
|
||||
|
||||
/*
|
||||
* Indices use 0-based numbering for the on-disk (and binary) format (consistent with C)
|
||||
* and are always sorted. Values come after indices.
|
||||
*/
|
||||
typedef struct SparseVector
|
||||
{
|
||||
int32 vl_len_; /* varlena header (do not touch directly!) */
|
||||
int32 dim; /* number of dimensions */
|
||||
int32 nnz;
|
||||
int32 unused;
|
||||
int32 nnz; /* number of non-zero elements */
|
||||
int32 unused; /* reserved for future use, always zero */
|
||||
int32 indices[FLEXIBLE_ARRAY_MEMBER];
|
||||
} SparseVector;
|
||||
|
||||
/* Use functions instead of macros to avoid double evaluation */
|
||||
|
||||
static inline Size
|
||||
SPARSEVEC_SIZE(int nnz)
|
||||
{
|
||||
return offsetof(SparseVector, indices) + (nnz * sizeof(int32)) + (nnz * sizeof(float));
|
||||
}
|
||||
|
||||
static inline float *
|
||||
SPARSEVEC_VALUES(SparseVector * x)
|
||||
{
|
||||
return (float *) (((char *) x) + offsetof(SparseVector, indices) + (x->nnz * sizeof(int32)));
|
||||
}
|
||||
|
||||
SparseVector *InitSparseVector(int dim, int nnz);
|
||||
|
||||
#endif
|
||||
|
||||
100
src/vector.c
100
src/vector.c
@@ -181,7 +181,7 @@ float_underflow_error(void)
|
||||
/*
|
||||
* Convert textual representation to internal representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_in);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_in);
|
||||
Datum
|
||||
vector_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -294,7 +294,7 @@ vector_in(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert internal representation to textual representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_out);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_out);
|
||||
Datum
|
||||
vector_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -348,7 +348,7 @@ PrintVector(char *msg, Vector * vector)
|
||||
/*
|
||||
* Convert type modifier
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_typmod_in);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_typmod_in);
|
||||
Datum
|
||||
vector_typmod_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -379,7 +379,7 @@ vector_typmod_in(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert external binary representation to internal representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_recv);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_recv);
|
||||
Datum
|
||||
vector_recv(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -413,7 +413,7 @@ vector_recv(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert internal representation to the external binary representation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_send);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_send);
|
||||
Datum
|
||||
vector_send(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -433,7 +433,7 @@ vector_send(PG_FUNCTION_ARGS)
|
||||
* Convert vector to vector
|
||||
* This is needed to check the type modifier
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector);
|
||||
Datum
|
||||
vector(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -448,7 +448,7 @@ vector(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert array to vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(array_to_vector);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(array_to_vector);
|
||||
Datum
|
||||
array_to_vector(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -522,7 +522,7 @@ array_to_vector(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert vector to float4[]
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_float4);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_to_float4);
|
||||
Datum
|
||||
vector_to_float4(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -546,7 +546,7 @@ vector_to_float4(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert half vector to vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_vector);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_to_vector);
|
||||
Datum
|
||||
halfvec_to_vector(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -584,7 +584,7 @@ VectorL2SquaredDistance(int dim, float *ax, float *bx)
|
||||
/*
|
||||
* Get the L2 distance between vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(l2_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(l2_distance);
|
||||
Datum
|
||||
l2_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -600,7 +600,7 @@ l2_distance(PG_FUNCTION_ARGS)
|
||||
* Get the L2 squared distance between vectors
|
||||
* This saves a sqrt calculation
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_l2_squared_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_l2_squared_distance);
|
||||
Datum
|
||||
vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -627,7 +627,7 @@ VectorInnerProduct(int dim, float *ax, float *bx)
|
||||
/*
|
||||
* Get the inner product of two vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(inner_product);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(inner_product);
|
||||
Datum
|
||||
inner_product(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -642,7 +642,7 @@ inner_product(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the negative inner product of two vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_negative_inner_product);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_negative_inner_product);
|
||||
Datum
|
||||
vector_negative_inner_product(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -676,7 +676,7 @@ VectorCosineSimilarity(int dim, float *ax, float *bx)
|
||||
/*
|
||||
* Get the cosine distance between two vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(cosine_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(cosine_distance);
|
||||
Datum
|
||||
cosine_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -708,7 +708,7 @@ cosine_distance(PG_FUNCTION_ARGS)
|
||||
* Currently uses angular distance since needs to satisfy triangle inequality
|
||||
* Assumes inputs are unit vectors (skips norm)
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_spherical_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_spherical_distance);
|
||||
Datum
|
||||
vector_spherical_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -745,7 +745,7 @@ VectorL1Distance(int dim, float *ax, float *bx)
|
||||
/*
|
||||
* Get the L1 distance between two vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(l1_distance);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(l1_distance);
|
||||
Datum
|
||||
l1_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -760,7 +760,7 @@ l1_distance(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the dimensions of a vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_dims);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_dims);
|
||||
Datum
|
||||
vector_dims(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -772,7 +772,7 @@ vector_dims(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get the L2 norm of a vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_norm);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_norm);
|
||||
Datum
|
||||
vector_norm(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -790,7 +790,7 @@ vector_norm(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Normalize a vector with the L2 norm
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(l2_normalize);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(l2_normalize);
|
||||
Datum
|
||||
l2_normalize(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -829,7 +829,7 @@ l2_normalize(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Add vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_add);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_add);
|
||||
Datum
|
||||
vector_add(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -862,7 +862,7 @@ vector_add(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Subtract vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_sub);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_sub);
|
||||
Datum
|
||||
vector_sub(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -895,7 +895,7 @@ vector_sub(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Multiply vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_mul);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_mul);
|
||||
Datum
|
||||
vector_mul(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -931,7 +931,7 @@ vector_mul(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Concatenate vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_concat);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_concat);
|
||||
Datum
|
||||
vector_concat(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -955,7 +955,7 @@ vector_concat(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Quantize a vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(binary_quantize);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(binary_quantize);
|
||||
Datum
|
||||
binary_quantize(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -973,24 +973,39 @@ binary_quantize(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Get a subvector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(subvector);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(subvector);
|
||||
Datum
|
||||
subvector(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||
int32 start = PG_GETARG_INT32(1);
|
||||
int32 count = PG_GETARG_INT32(2);
|
||||
int32 end = start + count;
|
||||
int32 end;
|
||||
float *ax = a->x;
|
||||
Vector *result;
|
||||
int dim;
|
||||
|
||||
if (count < 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||
errmsg("vector must have at least 1 dimension")));
|
||||
|
||||
/*
|
||||
* Check if (start + count > a->dim), avoiding integer overflow. a->dim
|
||||
* and count are both positive, so a->dim - count won't overflow.
|
||||
*/
|
||||
if (start > a->dim - count)
|
||||
end = a->dim + 1;
|
||||
else
|
||||
end = start + count;
|
||||
|
||||
/* Indexing starts at 1, like substring */
|
||||
if (start < 1)
|
||||
start = 1;
|
||||
|
||||
if (end > a->dim)
|
||||
end = a->dim + 1;
|
||||
else if (start > a->dim)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||
errmsg("vector must have at least 1 dimension")));
|
||||
|
||||
dim = end - start;
|
||||
CheckDim(dim);
|
||||
@@ -1032,7 +1047,7 @@ vector_cmp_internal(Vector * a, Vector * b)
|
||||
/*
|
||||
* Less than
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_lt);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_lt);
|
||||
Datum
|
||||
vector_lt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1045,7 +1060,7 @@ vector_lt(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Less than or equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_le);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_le);
|
||||
Datum
|
||||
vector_le(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1058,7 +1073,7 @@ vector_le(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_eq);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_eq);
|
||||
Datum
|
||||
vector_eq(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1071,7 +1086,7 @@ vector_eq(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Not equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ne);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_ne);
|
||||
Datum
|
||||
vector_ne(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1084,7 +1099,7 @@ vector_ne(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Greater than or equal
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ge);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_ge);
|
||||
Datum
|
||||
vector_ge(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1097,7 +1112,7 @@ vector_ge(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Greater than
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_gt);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_gt);
|
||||
Datum
|
||||
vector_gt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1110,7 +1125,7 @@ vector_gt(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Compare vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_cmp);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_cmp);
|
||||
Datum
|
||||
vector_cmp(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1123,7 +1138,7 @@ vector_cmp(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Accumulate vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_accum);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_accum);
|
||||
Datum
|
||||
vector_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1182,12 +1197,13 @@ vector_accum(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Combine vectors or half vectors
|
||||
* Combine vectors or half vectors (also used for halfvec_combine)
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_combine);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_combine);
|
||||
Datum
|
||||
vector_combine(PG_FUNCTION_ARGS)
|
||||
{
|
||||
/* Must also update parameters of halfvec_combine if modifying */
|
||||
ArrayType *statearray1 = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ArrayType *statearray2 = PG_GETARG_ARRAYTYPE_P(1);
|
||||
float8 *statevalues1;
|
||||
@@ -1254,7 +1270,7 @@ vector_combine(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Average vectors
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_avg);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_avg);
|
||||
Datum
|
||||
vector_avg(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1288,7 +1304,7 @@ vector_avg(PG_FUNCTION_ARGS)
|
||||
/*
|
||||
* Convert sparse vector to dense vector
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_to_vector);
|
||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_to_vector);
|
||||
Datum
|
||||
sparsevec_to_vector(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@@ -1303,7 +1319,7 @@ sparsevec_to_vector(PG_FUNCTION_ARGS)
|
||||
|
||||
result = InitVector(dim);
|
||||
for (int i = 0; i < svec->nnz; i++)
|
||||
result->x[svec->indices[i] - 1] = values[i];
|
||||
result->x[svec->indices[i]] = values[i];
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
12
src/vector.h
12
src/vector.h
@@ -1,8 +1,6 @@
|
||||
#ifndef VECTOR_H
|
||||
#define VECTOR_H
|
||||
|
||||
#include "fmgr.h"
|
||||
|
||||
#define VECTOR_MAX_DIM 16000
|
||||
|
||||
#define VECTOR_SIZE(_dim) (offsetof(Vector, x) + sizeof(float)*(_dim))
|
||||
@@ -14,13 +12,19 @@ typedef struct Vector
|
||||
{
|
||||
int32 vl_len_; /* varlena header (do not touch directly!) */
|
||||
int16 dim; /* number of dimensions */
|
||||
int16 unused;
|
||||
int16 unused; /* reserved for future use, always zero */
|
||||
float x[FLEXIBLE_ARRAY_MEMBER];
|
||||
} Vector;
|
||||
|
||||
Vector *InitVector(int dim);
|
||||
void PrintVector(char *msg, Vector * vector);
|
||||
int vector_cmp_internal(Vector * a, Vector * b);
|
||||
PGDLLEXPORT Datum l2_normalize(PG_FUNCTION_ARGS);
|
||||
|
||||
/* TODO Move to better place */
|
||||
#if PG_VERSION_NUM >= 160000
|
||||
#define FUNCTION_PREFIX
|
||||
#else
|
||||
#define FUNCTION_PREFIX PGDLLEXPORT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
61
test/expected/btree.out
Normal file
61
test/expected/btree.out
Normal file
@@ -0,0 +1,61 @@
|
||||
SET enable_seqscan = off;
|
||||
-- vector
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM t ORDER BY val;
|
||||
val
|
||||
---------
|
||||
[0,0,0]
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- halfvec
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM t ORDER BY val;
|
||||
val
|
||||
---------
|
||||
[0,0,0]
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- sparsevec
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
SELECT * FROM t WHERE val = '{1:1,2:2,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:2,3:3}/3
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM t ORDER BY val;
|
||||
val
|
||||
-----------------
|
||||
{}/3
|
||||
{1:1,2:1,3:1}/3
|
||||
{1:1,2:2,3:3}/3
|
||||
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,20 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM t ORDER BY val;
|
||||
val
|
||||
---------
|
||||
[0,0,0]
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,20 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
SELECT * FROM t WHERE val = '{1:1,2:2,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:2,3:3}/3
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM t ORDER BY val;
|
||||
val
|
||||
-----------------
|
||||
{}/3
|
||||
{1:1,2:1,3:1}/3
|
||||
{1:1,2:2,3:3}/3
|
||||
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,20 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM t ORDER BY val;
|
||||
val
|
||||
---------
|
||||
[0,0,0]
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,15 +1,50 @@
|
||||
CREATE TABLE t (val vector(3), val2 halfvec(3), val3 sparsevec(3));
|
||||
INSERT INTO t (val, val2, val3) VALUES ('[0,0,0]', '[0,0,0]', '{}/3'), ('[1,2,3]', '[1,2,3]', '{1:1,2:2,3:3}/3'), ('[1,1,1]', '[1,1,1]', '{1:1,2:1,3:1}/3'), (NULL, NULL, NULL);
|
||||
CREATE TABLE t2 (val vector(3), val2 halfvec(3), val3 sparsevec(3));
|
||||
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
||||
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
||||
-- vector
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE TABLE t2 (val vector(3));
|
||||
\copy t TO 'results/vector.bin' WITH (FORMAT binary)
|
||||
\copy t2 FROM 'results/vector.bin' WITH (FORMAT binary)
|
||||
SELECT * FROM t2 ORDER BY val;
|
||||
val | val2 | val3
|
||||
---------+---------+-----------------
|
||||
[0,0,0] | [0,0,0] | {}/3
|
||||
[1,1,1] | [1,1,1] | {1:1,2:1,3:1}/3
|
||||
[1,2,3] | [1,2,3] | {1:1,2:2,3:3}/3
|
||||
| |
|
||||
val
|
||||
---------
|
||||
[0,0,0]
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
DROP TABLE t2;
|
||||
-- halfvec
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE TABLE t2 (val halfvec(3));
|
||||
\copy t TO 'results/halfvec.bin' WITH (FORMAT binary)
|
||||
\copy t2 FROM 'results/halfvec.bin' WITH (FORMAT binary)
|
||||
SELECT * FROM t2 ORDER BY val;
|
||||
val
|
||||
---------
|
||||
[0,0,0]
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
DROP TABLE t2;
|
||||
-- sparsevec
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE TABLE t2 (val sparsevec(3));
|
||||
\copy t TO 'results/sparsevec.bin' WITH (FORMAT binary)
|
||||
\copy t2 FROM 'results/sparsevec.bin' WITH (FORMAT binary)
|
||||
SELECT * FROM t2 ORDER BY val;
|
||||
val
|
||||
-----------------
|
||||
{}/3
|
||||
{1:1,2:1,3:1}/3
|
||||
{1:1,2:2,3:3}/3
|
||||
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
@@ -1,3 +1,167 @@
|
||||
SELECT '[1,2,3]'::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[-1,-2,-3]'::halfvec;
|
||||
halfvec
|
||||
------------
|
||||
[-1,-2,-3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.,2.,3.]'::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT ' [ 1, 2 , 3 ] '::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.23456]'::halfvec;
|
||||
halfvec
|
||||
------------
|
||||
[1.234375]
|
||||
(1 row)
|
||||
|
||||
SELECT '[hello,1]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[hello,1]"
|
||||
LINE 1: SELECT '[hello,1]'::halfvec;
|
||||
^
|
||||
SELECT '[NaN,1]'::halfvec;
|
||||
ERROR: NaN not allowed in halfvec
|
||||
LINE 1: SELECT '[NaN,1]'::halfvec;
|
||||
^
|
||||
SELECT '[Infinity,1]'::halfvec;
|
||||
ERROR: infinite value not allowed in halfvec
|
||||
LINE 1: SELECT '[Infinity,1]'::halfvec;
|
||||
^
|
||||
SELECT '[-Infinity,1]'::halfvec;
|
||||
ERROR: infinite value not allowed in halfvec
|
||||
LINE 1: SELECT '[-Infinity,1]'::halfvec;
|
||||
^
|
||||
SELECT '[65519,-65519]'::halfvec;
|
||||
halfvec
|
||||
----------------
|
||||
[65504,-65504]
|
||||
(1 row)
|
||||
|
||||
SELECT '[65520,-65520]'::halfvec;
|
||||
ERROR: "65520" is out of range for type halfvec
|
||||
LINE 1: SELECT '[65520,-65520]'::halfvec;
|
||||
^
|
||||
SELECT '[1e-8,-1e-8]'::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[0,-0]
|
||||
(1 row)
|
||||
|
||||
SELECT '[4e38,1]'::halfvec;
|
||||
ERROR: "4e38" is out of range for type halfvec
|
||||
LINE 1: SELECT '[4e38,1]'::halfvec;
|
||||
^
|
||||
SELECT '[1e-46,1]'::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[0,1]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1,2,3'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1,2,3"
|
||||
LINE 1: SELECT '[1,2,3'::halfvec;
|
||||
^
|
||||
SELECT '[1,2,3]9'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1,2,3]9"
|
||||
LINE 1: SELECT '[1,2,3]9'::halfvec;
|
||||
^
|
||||
DETAIL: Junk after closing right brace.
|
||||
SELECT '1,2,3'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "1,2,3"
|
||||
LINE 1: SELECT '1,2,3'::halfvec;
|
||||
^
|
||||
DETAIL: Vector contents must start with "[".
|
||||
SELECT ''::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: ""
|
||||
LINE 1: SELECT ''::halfvec;
|
||||
^
|
||||
DETAIL: Vector contents must start with "[".
|
||||
SELECT '['::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "["
|
||||
LINE 1: SELECT '['::halfvec;
|
||||
^
|
||||
SELECT '[ '::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[ "
|
||||
LINE 1: SELECT '[ '::halfvec;
|
||||
^
|
||||
SELECT '[,'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[,"
|
||||
LINE 1: SELECT '[,'::halfvec;
|
||||
^
|
||||
SELECT '[]'::halfvec;
|
||||
ERROR: halfvec must have at least 1 dimension
|
||||
LINE 1: SELECT '[]'::halfvec;
|
||||
^
|
||||
SELECT '[ ]'::halfvec;
|
||||
ERROR: halfvec must have at least 1 dimension
|
||||
LINE 1: SELECT '[ ]'::halfvec;
|
||||
^
|
||||
SELECT '[,]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[,]"
|
||||
LINE 1: SELECT '[,]'::halfvec;
|
||||
^
|
||||
SELECT '[1,]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1,]"
|
||||
LINE 1: SELECT '[1,]'::halfvec;
|
||||
^
|
||||
SELECT '[1a]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1a]"
|
||||
LINE 1: SELECT '[1a]'::halfvec;
|
||||
^
|
||||
SELECT '[1,,3]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1,,3]"
|
||||
LINE 1: SELECT '[1,,3]'::halfvec;
|
||||
^
|
||||
SELECT '[1, ,3]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1, ,3]"
|
||||
LINE 1: SELECT '[1, ,3]'::halfvec;
|
||||
^
|
||||
SELECT '[1,2,3]'::halfvec(3);
|
||||
halfvec
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1,2,3]'::halfvec(2);
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
SELECT '[1,2,3]'::halfvec(3, 2);
|
||||
ERROR: invalid type modifier
|
||||
LINE 1: SELECT '[1,2,3]'::halfvec(3, 2);
|
||||
^
|
||||
SELECT '[1,2,3]'::halfvec('a');
|
||||
ERROR: invalid input syntax for type integer: "a"
|
||||
LINE 1: SELECT '[1,2,3]'::halfvec('a');
|
||||
^
|
||||
SELECT '[1,2,3]'::halfvec(0);
|
||||
ERROR: dimensions for type halfvec must be at least 1
|
||||
LINE 1: SELECT '[1,2,3]'::halfvec(0);
|
||||
^
|
||||
SELECT '[1,2,3]'::halfvec(16001);
|
||||
ERROR: dimensions for type halfvec cannot exceed 16000
|
||||
LINE 1: SELECT '[1,2,3]'::halfvec(16001);
|
||||
^
|
||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::halfvec[]);
|
||||
unnest
|
||||
---------
|
||||
[1,2,3]
|
||||
[4,5,6]
|
||||
(2 rows)
|
||||
|
||||
SELECT '{"[1,2,3]"}'::halfvec(2)[];
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
SELECT '[1,2,3]'::halfvec + '[4,5,6]';
|
||||
?column?
|
||||
----------
|
||||
@@ -176,6 +340,18 @@ SELECT l2_norm('[0,1]'::halfvec);
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('[0,0]'::halfvec);
|
||||
l2_norm
|
||||
---------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('[2]'::halfvec);
|
||||
l2_norm
|
||||
---------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('[0,0]'::halfvec, '[3,4]');
|
||||
l2_distance
|
||||
-------------
|
||||
@@ -394,6 +570,20 @@ SELECT subvector('[1,2,3,4,5]'::halfvec, 3, -1);
|
||||
ERROR: halfvec must have at least 1 dimension
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, -1, 2);
|
||||
ERROR: halfvec must have at least 1 dimension
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, 2147483647, 10);
|
||||
ERROR: halfvec must have at least 1 dimension
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 2147483647);
|
||||
subvector
|
||||
-----------
|
||||
[3,4,5]
|
||||
(1 row)
|
||||
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, -2147483644, 2147483647);
|
||||
subvector
|
||||
-----------
|
||||
[1,2]
|
||||
(1 row)
|
||||
|
||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]']) v;
|
||||
avg
|
||||
-----------
|
||||
@@ -1,164 +0,0 @@
|
||||
SELECT '[1,2,3]'::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[-1,-2,-3]'::halfvec;
|
||||
halfvec
|
||||
------------
|
||||
[-1,-2,-3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.,2.,3.]'::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT ' [ 1, 2 , 3 ] '::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.23456]'::halfvec;
|
||||
halfvec
|
||||
------------
|
||||
[1.234375]
|
||||
(1 row)
|
||||
|
||||
SELECT '[hello,1]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[hello,1]"
|
||||
LINE 1: SELECT '[hello,1]'::halfvec;
|
||||
^
|
||||
SELECT '[NaN,1]'::halfvec;
|
||||
ERROR: NaN not allowed in halfvec
|
||||
LINE 1: SELECT '[NaN,1]'::halfvec;
|
||||
^
|
||||
SELECT '[Infinity,1]'::halfvec;
|
||||
ERROR: infinite value not allowed in halfvec
|
||||
LINE 1: SELECT '[Infinity,1]'::halfvec;
|
||||
^
|
||||
SELECT '[-Infinity,1]'::halfvec;
|
||||
ERROR: infinite value not allowed in halfvec
|
||||
LINE 1: SELECT '[-Infinity,1]'::halfvec;
|
||||
^
|
||||
SELECT '[65519,-65519]'::halfvec;
|
||||
halfvec
|
||||
----------------
|
||||
[65504,-65504]
|
||||
(1 row)
|
||||
|
||||
SELECT '[65520,-65520]'::halfvec;
|
||||
ERROR: "65520" is out of range for type halfvec
|
||||
LINE 1: SELECT '[65520,-65520]'::halfvec;
|
||||
^
|
||||
SELECT '[1e-8,-1e-8]'::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[0,-0]
|
||||
(1 row)
|
||||
|
||||
SELECT '[4e38,1]'::halfvec;
|
||||
ERROR: "4e38" is out of range for type halfvec
|
||||
LINE 1: SELECT '[4e38,1]'::halfvec;
|
||||
^
|
||||
SELECT '[1e-46,1]'::halfvec;
|
||||
halfvec
|
||||
---------
|
||||
[0,1]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1,2,3'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1,2,3"
|
||||
LINE 1: SELECT '[1,2,3'::halfvec;
|
||||
^
|
||||
SELECT '[1,2,3]9'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1,2,3]9"
|
||||
LINE 1: SELECT '[1,2,3]9'::halfvec;
|
||||
^
|
||||
DETAIL: Junk after closing right brace.
|
||||
SELECT '1,2,3'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "1,2,3"
|
||||
LINE 1: SELECT '1,2,3'::halfvec;
|
||||
^
|
||||
DETAIL: Vector contents must start with "[".
|
||||
SELECT ''::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: ""
|
||||
LINE 1: SELECT ''::halfvec;
|
||||
^
|
||||
DETAIL: Vector contents must start with "[".
|
||||
SELECT '['::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "["
|
||||
LINE 1: SELECT '['::halfvec;
|
||||
^
|
||||
SELECT '[ '::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[ "
|
||||
LINE 1: SELECT '[ '::halfvec;
|
||||
^
|
||||
SELECT '[,'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[,"
|
||||
LINE 1: SELECT '[,'::halfvec;
|
||||
^
|
||||
SELECT '[]'::halfvec;
|
||||
ERROR: halfvec must have at least 1 dimension
|
||||
LINE 1: SELECT '[]'::halfvec;
|
||||
^
|
||||
SELECT '[ ]'::halfvec;
|
||||
ERROR: halfvec must have at least 1 dimension
|
||||
LINE 1: SELECT '[ ]'::halfvec;
|
||||
^
|
||||
SELECT '[,]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[,]"
|
||||
LINE 1: SELECT '[,]'::halfvec;
|
||||
^
|
||||
SELECT '[1,]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1,]"
|
||||
LINE 1: SELECT '[1,]'::halfvec;
|
||||
^
|
||||
SELECT '[1a]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1a]"
|
||||
LINE 1: SELECT '[1a]'::halfvec;
|
||||
^
|
||||
SELECT '[1,,3]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1,,3]"
|
||||
LINE 1: SELECT '[1,,3]'::halfvec;
|
||||
^
|
||||
SELECT '[1, ,3]'::halfvec;
|
||||
ERROR: invalid input syntax for type halfvec: "[1, ,3]"
|
||||
LINE 1: SELECT '[1, ,3]'::halfvec;
|
||||
^
|
||||
SELECT '[1,2,3]'::halfvec(3);
|
||||
halfvec
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1,2,3]'::halfvec(2);
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
SELECT '[1,2,3]'::halfvec(3, 2);
|
||||
ERROR: invalid type modifier
|
||||
LINE 1: SELECT '[1,2,3]'::halfvec(3, 2);
|
||||
^
|
||||
SELECT '[1,2,3]'::halfvec('a');
|
||||
ERROR: invalid input syntax for type integer: "a"
|
||||
LINE 1: SELECT '[1,2,3]'::halfvec('a');
|
||||
^
|
||||
SELECT '[1,2,3]'::halfvec(0);
|
||||
ERROR: dimensions for type halfvec must be at least 1
|
||||
LINE 1: SELECT '[1,2,3]'::halfvec(0);
|
||||
^
|
||||
SELECT '[1,2,3]'::halfvec(16001);
|
||||
ERROR: dimensions for type halfvec cannot exceed 16000
|
||||
LINE 1: SELECT '[1,2,3]'::halfvec(16001);
|
||||
^
|
||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::halfvec[]);
|
||||
unnest
|
||||
---------
|
||||
[1,2,3]
|
||||
[4,5,6]
|
||||
(2 rows)
|
||||
|
||||
SELECT '{"[1,2,3]"}'::halfvec(2)[];
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
@@ -1,4 +1,5 @@
|
||||
SET enable_seqscan = off;
|
||||
-- hamming
|
||||
CREATE TABLE t (val bit(3));
|
||||
INSERT INTO t (val) VALUES (B'000'), (B'100'), (B'111'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
||||
@@ -19,7 +20,28 @@ SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <~> (SELECT NULL::bit)) t2;
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- TODO move
|
||||
-- jaccard
|
||||
CREATE TABLE t (val bit(4));
|
||||
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val bit_jaccard_ops);
|
||||
INSERT INTO t (val) VALUES (B'1110');
|
||||
SELECT * FROM t ORDER BY val <%> B'1111';
|
||||
val
|
||||
------
|
||||
1111
|
||||
1110
|
||||
1100
|
||||
0000
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- varbit
|
||||
CREATE TABLE t (val varbit(3));
|
||||
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
||||
ERROR: type not supported for hnsw index
|
||||
@@ -1,21 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val bit(4));
|
||||
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val bit_jaccard_ops);
|
||||
INSERT INTO t (val) VALUES (B'1110');
|
||||
SELECT * FROM t ORDER BY val <%> B'1111';
|
||||
val
|
||||
------
|
||||
1111
|
||||
1110
|
||||
1100
|
||||
0000
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
102
test/expected/hnsw_halfvec.out
Normal file
102
test/expected/hnsw_halfvec.out
Normal file
@@ -0,0 +1,102 @@
|
||||
SET enable_seqscan = off;
|
||||
-- L2
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_l2_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- inner product
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_ip_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,4]
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- cosine
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_cosine_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- L1
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_l1_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,26 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_cosine_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,21 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_ip_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,4]
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,21 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_l1_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,33 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_l2_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,26 +0,0 @@
|
||||
CREATE TABLE t (val vector(3));
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 1);
|
||||
ERROR: value 1 out of bounds for option "m"
|
||||
DETAIL: Valid values are between "2" and "100".
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 101);
|
||||
ERROR: value 101 out of bounds for option "m"
|
||||
DETAIL: Valid values are between "2" and "100".
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (ef_construction = 3);
|
||||
ERROR: value 3 out of bounds for option "ef_construction"
|
||||
DETAIL: Valid values are between "4" and "1000".
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (ef_construction = 1001);
|
||||
ERROR: value 1001 out of bounds for option "ef_construction"
|
||||
DETAIL: Valid values are between "4" and "1000".
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 16, ef_construction = 31);
|
||||
ERROR: ef_construction must be greater than or equal to 2 * m
|
||||
SHOW hnsw.ef_search;
|
||||
hnsw.ef_search
|
||||
----------------
|
||||
40
|
||||
(1 row)
|
||||
|
||||
SET hnsw.ef_search = 0;
|
||||
ERROR: 0 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
|
||||
SET hnsw.ef_search = 1001;
|
||||
ERROR: 1001 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
|
||||
DROP TABLE t;
|
||||
112
test/expected/hnsw_sparsevec.out
Normal file
112
test/expected/hnsw_sparsevec.out
Normal file
@@ -0,0 +1,112 @@
|
||||
SET enable_seqscan = off;
|
||||
-- L2
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||
SELECT * FROM t ORDER BY val <-> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:2,3:3}/3
|
||||
{1:1,2:2,3:4}/3
|
||||
{1:1,2:1,3:1}/3
|
||||
{}/3
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
SELECT * FROM t ORDER BY val <-> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- inner product
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops);
|
||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||
SELECT * FROM t ORDER BY val <#> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:2,3:4}/3
|
||||
{1:1,2:2,3:3}/3
|
||||
{1:1,2:1,3:1}/3
|
||||
{}/3
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- cosine
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops);
|
||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||
SELECT * FROM t ORDER BY val <=> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:1,3:1}/3
|
||||
{1:1,2:2,3:3}/3
|
||||
{1:1,2:2,3:4}/3
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}/3') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::sparsevec)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- L1
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_l1_ops);
|
||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||
SELECT * FROM t ORDER BY val <+> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:2,3:3}/3
|
||||
{1:1,2:2,3:4}/3
|
||||
{1:1,2:1,3:1}/3
|
||||
{}/3
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::sparsevec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- non-zero elements
|
||||
CREATE TABLE t (val sparsevec(1001));
|
||||
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
||||
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index
|
||||
TRUNCATE t;
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
||||
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
|
||||
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index
|
||||
DROP TABLE t;
|
||||
@@ -1,26 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops);
|
||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||
SELECT * FROM t ORDER BY val <=> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:1,3:1}/3
|
||||
{1:1,2:2,3:3}/3
|
||||
{1:1,2:2,3:4}/3
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}/3') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::sparsevec)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,21 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops);
|
||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||
SELECT * FROM t ORDER BY val <#> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:2,3:4}/3
|
||||
{1:1,2:2,3:3}/3
|
||||
{1:1,2:1,3:1}/3
|
||||
{}/3
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,21 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_l1_ops);
|
||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||
SELECT * FROM t ORDER BY val <+> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:2,3:3}/3
|
||||
{1:1,2:2,3:4}/3
|
||||
{1:1,2:1,3:1}/3
|
||||
{}/3
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::sparsevec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,43 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||
SELECT * FROM t ORDER BY val <-> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----------------
|
||||
{1:1,2:2,3:3}/3
|
||||
{1:1,2:2,3:4}/3
|
||||
{1:1,2:1,3:1}/3
|
||||
{}/3
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
SELECT * FROM t ORDER BY val <-> '{1:3,2:3,3:3}/3';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- TODO move
|
||||
CREATE TABLE t (val sparsevec(1001));
|
||||
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
||||
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index
|
||||
TRUNCATE t;
|
||||
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
||||
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
|
||||
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index
|
||||
DROP TABLE t;
|
||||
@@ -1,13 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE UNLOGGED TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops);
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(3 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
142
test/expected/hnsw_vector.out
Normal file
142
test/expected/hnsw_vector.out
Normal file
@@ -0,0 +1,142 @@
|
||||
SET enable_seqscan = off;
|
||||
-- L2
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- inner product
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_ip_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,4]
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- cosine
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_cosine_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- L1
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_l1_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- unlogged
|
||||
CREATE UNLOGGED TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops);
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(3 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- options
|
||||
CREATE TABLE t (val vector(3));
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 1);
|
||||
ERROR: value 1 out of bounds for option "m"
|
||||
DETAIL: Valid values are between "2" and "100".
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 101);
|
||||
ERROR: value 101 out of bounds for option "m"
|
||||
DETAIL: Valid values are between "2" and "100".
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (ef_construction = 3);
|
||||
ERROR: value 3 out of bounds for option "ef_construction"
|
||||
DETAIL: Valid values are between "4" and "1000".
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (ef_construction = 1001);
|
||||
ERROR: value 1001 out of bounds for option "ef_construction"
|
||||
DETAIL: Valid values are between "4" and "1000".
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 16, ef_construction = 31);
|
||||
ERROR: ef_construction must be greater than or equal to 2 * m
|
||||
SHOW hnsw.ef_search;
|
||||
hnsw.ef_search
|
||||
----------------
|
||||
40
|
||||
(1 row)
|
||||
|
||||
SET hnsw.ef_search = 0;
|
||||
ERROR: 0 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
|
||||
SET hnsw.ef_search = 1001;
|
||||
ERROR: 1001 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
|
||||
DROP TABLE t;
|
||||
@@ -1,26 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_cosine_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,21 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_ip_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,4]
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,21 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_l1_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,33 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,4 +1,5 @@
|
||||
SET enable_seqscan = off;
|
||||
-- hamming
|
||||
CREATE TABLE t (val bit(3));
|
||||
INSERT INTO t (val) VALUES (B'000'), (B'100'), (B'111'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val bit_hamming_ops) WITH (lists = 1);
|
||||
@@ -19,7 +20,7 @@ SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <~> (SELECT NULL::bit)) t2;
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- TODO move
|
||||
-- varbit
|
||||
CREATE TABLE t (val varbit(3));
|
||||
CREATE INDEX ON t USING ivfflat (val bit_hamming_ops) WITH (lists = 1);
|
||||
ERROR: type not supported for ivfflat index
|
||||
84
test/expected/ivfflat_halfvec.out
Normal file
84
test/expected/ivfflat_halfvec.out
Normal file
@@ -0,0 +1,84 @@
|
||||
SET enable_seqscan = off;
|
||||
-- L2
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val halfvec_l2_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
NOTICE: ivfflat index created with little data
|
||||
DETAIL: This will cause low recall.
|
||||
HINT: Drop the index until the table has more data.
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- inner product
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val halfvec_ip_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,4]
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- cosine
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val halfvec_cosine_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,26 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val halfvec_cosine_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,21 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val halfvec_ip_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,4]
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,36 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val halfvec_l2_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
NOTICE: ivfflat index created with little data
|
||||
DETAIL: This will cause low recall.
|
||||
HINT: Drop the index until the table has more data.
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,14 +0,0 @@
|
||||
CREATE TABLE t (val vector(3));
|
||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 0);
|
||||
ERROR: value 0 out of bounds for option "lists"
|
||||
DETAIL: Valid values are between "1" and "32768".
|
||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 32769);
|
||||
ERROR: value 32769 out of bounds for option "lists"
|
||||
DETAIL: Valid values are between "1" and "32768".
|
||||
SHOW ivfflat.probes;
|
||||
ivfflat.probes
|
||||
----------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,13 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE UNLOGGED TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 1);
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(3 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
112
test/expected/ivfflat_vector.out
Normal file
112
test/expected/ivfflat_vector.out
Normal file
@@ -0,0 +1,112 @@
|
||||
SET enable_seqscan = off;
|
||||
-- L2
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
NOTICE: ivfflat index created with little data
|
||||
DETAIL: This will cause low recall.
|
||||
HINT: Drop the index until the table has more data.
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- inner product
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val vector_ip_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,4]
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- cosine
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val vector_cosine_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
-- unlogged
|
||||
CREATE UNLOGGED TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 1);
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(3 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
-- options
|
||||
CREATE TABLE t (val vector(3));
|
||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 0);
|
||||
ERROR: value 0 out of bounds for option "lists"
|
||||
DETAIL: Valid values are between "1" and "32768".
|
||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 32769);
|
||||
ERROR: value 32769 out of bounds for option "lists"
|
||||
DETAIL: Valid values are between "1" and "32768".
|
||||
SHOW ivfflat.probes;
|
||||
ivfflat.probes
|
||||
----------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,26 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val vector_cosine_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,1,1]
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
(3 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,21 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val vector_ip_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,4]
|
||||
[1,2,3]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,36 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 1);
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
---------
|
||||
[1,2,3]
|
||||
[1,2,4]
|
||||
[1,1,1]
|
||||
[0,0,0]
|
||||
(4 rows)
|
||||
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector)) t2;
|
||||
count
|
||||
-------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM t;
|
||||
count
|
||||
-------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
TRUNCATE t;
|
||||
NOTICE: ivfflat index created with little data
|
||||
DETAIL: This will cause low recall.
|
||||
HINT: Drop the index until the table has more data.
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
val
|
||||
-----
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t;
|
||||
653
test/expected/sparsevec.out
Normal file
653
test/expected/sparsevec.out
Normal file
@@ -0,0 +1,653 @@
|
||||
SELECT '{1:1.5,3:3.5}/5'::sparsevec;
|
||||
sparsevec
|
||||
-----------------
|
||||
{1:1.5,3:3.5}/5
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:-2,3:-4}/5'::sparsevec;
|
||||
sparsevec
|
||||
---------------
|
||||
{1:-2,3:-4}/5
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:2.,3:4.}/5'::sparsevec;
|
||||
sparsevec
|
||||
-------------
|
||||
{1:2,3:4}/5
|
||||
(1 row)
|
||||
|
||||
SELECT ' { 1 : 1.5 , 3 : 3.5 } / 5 '::sparsevec;
|
||||
sparsevec
|
||||
-----------------
|
||||
{1:1.5,3:3.5}/5
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1.23456}/1'::sparsevec;
|
||||
sparsevec
|
||||
---------------
|
||||
{1:1.23456}/1
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:hello,2:1}/2'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1:hello,2:1}/2"
|
||||
LINE 1: SELECT '{1:hello,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:NaN,2:1}/2'::sparsevec;
|
||||
ERROR: NaN not allowed in sparsevec
|
||||
LINE 1: SELECT '{1:NaN,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:Infinity,2:1}/2'::sparsevec;
|
||||
ERROR: infinite value not allowed in sparsevec
|
||||
LINE 1: SELECT '{1:Infinity,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:-Infinity,2:1}/2'::sparsevec;
|
||||
ERROR: infinite value not allowed in sparsevec
|
||||
LINE 1: SELECT '{1:-Infinity,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1.5e38,2:-1.5e38}/2'::sparsevec;
|
||||
sparsevec
|
||||
--------------------------
|
||||
{1:1.5e+38,2:-1.5e+38}/2
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1.5e+38,2:-1.5e+38}/2'::sparsevec;
|
||||
sparsevec
|
||||
--------------------------
|
||||
{1:1.5e+38,2:-1.5e+38}/2
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1.5e-38,2:-1.5e-38}/2'::sparsevec;
|
||||
sparsevec
|
||||
--------------------------
|
||||
{1:1.5e-38,2:-1.5e-38}/2
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:4e38,2:1}/2'::sparsevec;
|
||||
ERROR: "4e38" is out of range for type sparsevec
|
||||
LINE 1: SELECT '{1:4e38,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:-4e38,2:1}/2'::sparsevec;
|
||||
ERROR: "-4e38" is out of range for type sparsevec
|
||||
LINE 1: SELECT '{1:-4e38,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1e-46,2:1}/2'::sparsevec;
|
||||
ERROR: "1e-46" is out of range for type sparsevec
|
||||
LINE 1: SELECT '{1:1e-46,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:-1e-46,2:1}/2'::sparsevec;
|
||||
ERROR: "-1e-46" is out of range for type sparsevec
|
||||
LINE 1: SELECT '{1:-1e-46,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT ''::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: ""
|
||||
LINE 1: SELECT ''::sparsevec;
|
||||
^
|
||||
DETAIL: Vector contents must start with "{".
|
||||
SELECT '{'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{"
|
||||
LINE 1: SELECT '{'::sparsevec;
|
||||
^
|
||||
SELECT '{ '::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{ "
|
||||
LINE 1: SELECT '{ '::sparsevec;
|
||||
^
|
||||
SELECT '{:'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{:"
|
||||
LINE 1: SELECT '{:'::sparsevec;
|
||||
^
|
||||
SELECT '{,'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{,"
|
||||
LINE 1: SELECT '{,'::sparsevec;
|
||||
^
|
||||
SELECT '{}'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{}"
|
||||
LINE 1: SELECT '{}'::sparsevec;
|
||||
^
|
||||
DETAIL: Unexpected end of input.
|
||||
SELECT '{}/'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{}/"
|
||||
LINE 1: SELECT '{}/'::sparsevec;
|
||||
^
|
||||
SELECT '{}/1'::sparsevec;
|
||||
sparsevec
|
||||
-----------
|
||||
{}/1
|
||||
(1 row)
|
||||
|
||||
SELECT '{}/1a'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{}/1a"
|
||||
LINE 1: SELECT '{}/1a'::sparsevec;
|
||||
^
|
||||
DETAIL: Junk after closing.
|
||||
SELECT '{ }/1'::sparsevec;
|
||||
sparsevec
|
||||
-----------
|
||||
{}/1
|
||||
(1 row)
|
||||
|
||||
SELECT '{:}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{:}/1"
|
||||
LINE 1: SELECT '{:}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{,}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{,}/1"
|
||||
LINE 1: SELECT '{,}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1,}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1,}/1"
|
||||
LINE 1: SELECT '{1,}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{:1}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{:1}/1"
|
||||
LINE 1: SELECT '{:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1:}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1:}/1"
|
||||
LINE 1: SELECT '{1:}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1a:1}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1a:1}/1"
|
||||
LINE 1: SELECT '{1a:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1a}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1:1a}/1"
|
||||
LINE 1: SELECT '{1:1a}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1,}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1:1,}/1"
|
||||
LINE 1: SELECT '{1:1,}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1:0,2:1,3:0}/3'::sparsevec;
|
||||
sparsevec
|
||||
-----------
|
||||
{2:1}/3
|
||||
(1 row)
|
||||
|
||||
SELECT '{2:1,1:1}/2'::sparsevec;
|
||||
sparsevec
|
||||
-------------
|
||||
{1:1,2:1}/2
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,1:1}/2'::sparsevec;
|
||||
ERROR: sparsevec indices must not contain duplicates
|
||||
LINE 1: SELECT '{1:1,1:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1,2:1,1:1}/2'::sparsevec;
|
||||
ERROR: sparsevec indices must not contain duplicates
|
||||
LINE 1: SELECT '{1:1,2:1,1:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{}/5'::sparsevec;
|
||||
sparsevec
|
||||
-----------
|
||||
{}/5
|
||||
(1 row)
|
||||
|
||||
SELECT '{}/-1'::sparsevec;
|
||||
ERROR: sparsevec must have at least 1 dimension
|
||||
LINE 1: SELECT '{}/-1'::sparsevec;
|
||||
^
|
||||
SELECT '{}/1000000001'::sparsevec;
|
||||
ERROR: sparsevec cannot have more than 1000000000 dimensions
|
||||
LINE 1: SELECT '{}/1000000001'::sparsevec;
|
||||
^
|
||||
SELECT '{}/2147483648'::sparsevec;
|
||||
ERROR: sparsevec cannot have more than 1000000000 dimensions
|
||||
LINE 1: SELECT '{}/2147483648'::sparsevec;
|
||||
^
|
||||
SELECT '{}/-2147483649'::sparsevec;
|
||||
ERROR: sparsevec must have at least 1 dimension
|
||||
LINE 1: SELECT '{}/-2147483649'::sparsevec;
|
||||
^
|
||||
SELECT '{}/9223372036854775808'::sparsevec;
|
||||
ERROR: sparsevec cannot have more than 1000000000 dimensions
|
||||
LINE 1: SELECT '{}/9223372036854775808'::sparsevec;
|
||||
^
|
||||
SELECT '{}/-9223372036854775809'::sparsevec;
|
||||
ERROR: sparsevec must have at least 1 dimension
|
||||
LINE 1: SELECT '{}/-9223372036854775809'::sparsevec;
|
||||
^
|
||||
SELECT '{2147483647:1}/1'::sparsevec;
|
||||
ERROR: sparsevec index out of bounds
|
||||
LINE 1: SELECT '{2147483647:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{2147483648:1}/1'::sparsevec;
|
||||
ERROR: sparsevec index out of bounds
|
||||
LINE 1: SELECT '{2147483648:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{-2147483648:1}/1'::sparsevec;
|
||||
ERROR: sparsevec index out of bounds
|
||||
LINE 1: SELECT '{-2147483648:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{-2147483649:1}/1'::sparsevec;
|
||||
ERROR: sparsevec index out of bounds
|
||||
LINE 1: SELECT '{-2147483649:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{0:1}/1'::sparsevec;
|
||||
ERROR: sparsevec index out of bounds
|
||||
LINE 1: SELECT '{0:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{2:1}/1'::sparsevec;
|
||||
ERROR: sparsevec index out of bounds
|
||||
LINE 1: SELECT '{2:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{}/3'::sparsevec(3);
|
||||
sparsevec
|
||||
-----------
|
||||
{}/3
|
||||
(1 row)
|
||||
|
||||
SELECT '{}/3'::sparsevec(2);
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
SELECT '{}/3'::sparsevec(3, 2);
|
||||
ERROR: invalid type modifier
|
||||
LINE 1: SELECT '{}/3'::sparsevec(3, 2);
|
||||
^
|
||||
SELECT '{}/3'::sparsevec('a');
|
||||
ERROR: invalid input syntax for type integer: "a"
|
||||
LINE 1: SELECT '{}/3'::sparsevec('a');
|
||||
^
|
||||
SELECT '{}/3'::sparsevec(0);
|
||||
ERROR: dimensions for type sparsevec must be at least 1
|
||||
LINE 1: SELECT '{}/3'::sparsevec(0);
|
||||
^
|
||||
SELECT '{}/3'::sparsevec(1000000001);
|
||||
ERROR: dimensions for type sparsevec cannot exceed 1000000000
|
||||
LINE 1: SELECT '{}/3'::sparsevec(1000000001);
|
||||
^
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2,3:3}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{}/3', '{1:1,2:2,3:3}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
-1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:1,2:2,3:3}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
-1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2}/2');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:2,2:3,3:4}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
-1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:2,2:3}/2', '{1:1,2:2,3:3}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT round(l2_norm('{1:1,2:1}/2'::sparsevec)::numeric, 5);
|
||||
round
|
||||
---------
|
||||
1.41421
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('{1:3,2:4}/2'::sparsevec);
|
||||
l2_norm
|
||||
---------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('{2:1}/2'::sparsevec);
|
||||
l2_norm
|
||||
---------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('{1:3e37,2:4e37}/2'::sparsevec)::real;
|
||||
l2_norm
|
||||
---------
|
||||
5e+37
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('{}/2'::sparsevec);
|
||||
l2_norm
|
||||
---------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('{1:2}/1'::sparsevec);
|
||||
l2_norm
|
||||
---------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
|
||||
l2_distance
|
||||
-------------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('{1:3}/2'::sparsevec, '{2:4}/2');
|
||||
l2_distance
|
||||
-------------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('{2:4}/2'::sparsevec, '{1:3}/2');
|
||||
l2_distance
|
||||
-------------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('{1:3,2:4}/2'::sparsevec, '{}/2');
|
||||
l2_distance
|
||||
-------------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('{}/2'::sparsevec, '{2:1}/2');
|
||||
l2_distance
|
||||
-------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT '{}/2'::sparsevec <-> '{1:3,2:4}/2';
|
||||
?column?
|
||||
----------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
|
||||
inner_product
|
||||
---------------
|
||||
10
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
||||
ERROR: different sparsevec dimensions 2 and 1
|
||||
SELECT inner_product('{1:1,3:3}/4'::sparsevec, '{2:2,4:4}/4');
|
||||
inner_product
|
||||
---------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{2:2,4:4}/4'::sparsevec, '{1:1,3:3}/4');
|
||||
inner_product
|
||||
---------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{1:1,3:3,5:5}/5'::sparsevec, '{2:4,3:6,4:8}/5');
|
||||
inner_product
|
||||
---------------
|
||||
18
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{1:1}/2'::sparsevec, '{}/2');
|
||||
inner_product
|
||||
---------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{}/2'::sparsevec, '{1:1}/2');
|
||||
inner_product
|
||||
---------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
|
||||
inner_product
|
||||
---------------
|
||||
Infinity
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{1:1,3:3,5:5}/5'::sparsevec, '{2:4,3:6,4:8}/5');
|
||||
inner_product
|
||||
---------------
|
||||
18
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2}/2'::sparsevec <#> '{1:3,2:4}/2';
|
||||
?column?
|
||||
----------
|
||||
-11
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
NaN
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1,2:1}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1}/2'::sparsevec, '{2:2}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1,2:-1}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:2}/2'::sparsevec, '{2:2}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{2:2}/2'::sparsevec, '{1:2}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
||||
ERROR: different sparsevec dimensions 2 and 1
|
||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1.1,2:1.1}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1.1,2:-1.1}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
|
||||
cosine_distance
|
||||
-----------------
|
||||
NaN
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{}/1'::sparsevec, '{}/1');
|
||||
cosine_distance
|
||||
-----------------
|
||||
NaN
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2}/2'::sparsevec <=> '{1:2,2:4}/2';
|
||||
?column?
|
||||
----------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
|
||||
l1_distance
|
||||
-------------
|
||||
7
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{}/2'::sparsevec, '{2:1}/2');
|
||||
l1_distance
|
||||
-------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
||||
ERROR: different sparsevec dimensions 2 and 1
|
||||
SELECT l1_distance('{1:3e38}/1'::sparsevec, '{1:-3e38}/1');
|
||||
l1_distance
|
||||
-------------
|
||||
Infinity
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{1:1,3:3,5:5,7:7}/8'::sparsevec, '{2:2,4:4,6:6,8:8}/8');
|
||||
l1_distance
|
||||
-------------
|
||||
36
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{1:1,3:3,5:5,7:7,9:9}/9'::sparsevec, '{2:2,4:4,6:6,8:8}/9');
|
||||
l1_distance
|
||||
-------------
|
||||
45
|
||||
(1 row)
|
||||
|
||||
SELECT '{}/2'::sparsevec <+> '{1:3,2:4}/2';
|
||||
?column?
|
||||
----------
|
||||
7
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{1:3,2:4}/2'::sparsevec);
|
||||
l2_normalize
|
||||
-----------------
|
||||
{1:0.6,2:0.8}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{1:3}/2'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{1:1}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{2:0.1}/2'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{2:1}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{}/2'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{1:3e38}/1'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{1:1}/1
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{1:3e38,2:1e-37}/2'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{1:1}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{2:3e37,4:3e-37,6:4e37,8:4e-37}/9'::sparsevec);
|
||||
l2_normalize
|
||||
-----------------
|
||||
{2:0.6,6:0.8}/9
|
||||
(1 row)
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2,3:3}/3';
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2}/2';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2,3:3}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{}/3', '{1:1,2:2,3:3}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
-1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:1,2:2,3:3}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
-1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2}/2');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:2,2:3,3:4}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
-1
|
||||
(1 row)
|
||||
|
||||
SELECT sparsevec_cmp('{1:2,2:3}/2', '{1:1,2:2,3:3}/3');
|
||||
sparsevec_cmp
|
||||
---------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT round(l2_norm('{1:1,2:1}/2'::sparsevec)::numeric, 5);
|
||||
round
|
||||
---------
|
||||
1.41421
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('{1:3,2:4}/2'::sparsevec);
|
||||
l2_norm
|
||||
---------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('{2:1}/2'::sparsevec);
|
||||
l2_norm
|
||||
---------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT l2_norm('{1:3e37,2:4e37}/2'::sparsevec)::real;
|
||||
l2_norm
|
||||
---------
|
||||
5e+37
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
|
||||
l2_distance
|
||||
-------------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('{1:3}/2'::sparsevec, '{2:4}/2');
|
||||
l2_distance
|
||||
-------------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('{2:4}/2'::sparsevec, '{1:3}/2');
|
||||
l2_distance
|
||||
-------------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('{}/2'::sparsevec, '{2:1}/2');
|
||||
l2_distance
|
||||
-------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT '{}/2'::sparsevec <-> '{1:3,2:4}/2';
|
||||
?column?
|
||||
----------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
|
||||
inner_product
|
||||
---------------
|
||||
10
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
||||
ERROR: different sparsevec dimensions 2 and 1
|
||||
SELECT inner_product('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
|
||||
inner_product
|
||||
---------------
|
||||
Infinity
|
||||
(1 row)
|
||||
|
||||
SELECT inner_product('{1:1,3:3,5:5}/5'::sparsevec, '{2:4,3:6,4:8}/5');
|
||||
inner_product
|
||||
---------------
|
||||
18
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2}/2'::sparsevec <#> '{1:3,2:4}/2';
|
||||
?column?
|
||||
----------
|
||||
-11
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
NaN
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1,2:1}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1}/2'::sparsevec, '{2:2}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1,2:-1}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:2}/2'::sparsevec, '{2:2}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
||||
ERROR: different sparsevec dimensions 2 and 1
|
||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1.1,2:1.1}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1.1,2:-1.1}/2');
|
||||
cosine_distance
|
||||
-----------------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
|
||||
cosine_distance
|
||||
-----------------
|
||||
NaN
|
||||
(1 row)
|
||||
|
||||
SELECT cosine_distance('{}/1'::sparsevec, '{}/1');
|
||||
cosine_distance
|
||||
-----------------
|
||||
NaN
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,2:2}/2'::sparsevec <=> '{1:2,2:4}/2';
|
||||
?column?
|
||||
----------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
|
||||
l1_distance
|
||||
-------------
|
||||
7
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{}/2'::sparsevec, '{2:1}/2');
|
||||
l1_distance
|
||||
-------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
||||
ERROR: different sparsevec dimensions 2 and 1
|
||||
SELECT l1_distance('{1:3e38}/1'::sparsevec, '{1:-3e38}/1');
|
||||
l1_distance
|
||||
-------------
|
||||
Infinity
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{1:1,3:3,5:5,7:7}/8'::sparsevec, '{2:2,4:4,6:6,8:8}/8');
|
||||
l1_distance
|
||||
-------------
|
||||
36
|
||||
(1 row)
|
||||
|
||||
SELECT l1_distance('{1:1,3:3,5:5,7:7,9:9}/9'::sparsevec, '{2:2,4:4,6:6,8:8}/9');
|
||||
l1_distance
|
||||
-------------
|
||||
45
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{1:3,2:4}/2'::sparsevec);
|
||||
l2_normalize
|
||||
-----------------
|
||||
{1:0.6,2:0.8}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{1:3}/2'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{1:1}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{2:0.1}/2'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{2:1}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{}/2'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{1:3e38}/1'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{1:1}/1
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{1:3e38,2:1e-37}/2'::sparsevec);
|
||||
l2_normalize
|
||||
--------------
|
||||
{1:1}/2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_normalize('{2:3e37,4:3e-37,6:4e37,8:4e-37}/9'::sparsevec);
|
||||
l2_normalize
|
||||
-----------------
|
||||
{2:0.6,6:0.8}/9
|
||||
(1 row)
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
SELECT '{1:1.5,3:3.5}/5'::sparsevec;
|
||||
sparsevec
|
||||
-----------------
|
||||
{1:1.5,3:3.5}/5
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:-2,3:-4}/5'::sparsevec;
|
||||
sparsevec
|
||||
---------------
|
||||
{1:-2,3:-4}/5
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:2.,3:4.}/5'::sparsevec;
|
||||
sparsevec
|
||||
-------------
|
||||
{1:2,3:4}/5
|
||||
(1 row)
|
||||
|
||||
SELECT ' { 1 : 1.5 , 3 : 3.5 } / 5 '::sparsevec;
|
||||
sparsevec
|
||||
-----------------
|
||||
{1:1.5,3:3.5}/5
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1.23456}/1'::sparsevec;
|
||||
sparsevec
|
||||
---------------
|
||||
{1:1.23456}/1
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:hello,2:1}/2'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1:hello,2:1}/2"
|
||||
LINE 1: SELECT '{1:hello,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:NaN,2:1}/2'::sparsevec;
|
||||
ERROR: NaN not allowed in sparsevec
|
||||
LINE 1: SELECT '{1:NaN,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:Infinity,2:1}/2'::sparsevec;
|
||||
ERROR: infinite value not allowed in sparsevec
|
||||
LINE 1: SELECT '{1:Infinity,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:-Infinity,2:1}/2'::sparsevec;
|
||||
ERROR: infinite value not allowed in sparsevec
|
||||
LINE 1: SELECT '{1:-Infinity,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1.5e38,2:-1.5e38}/2'::sparsevec;
|
||||
sparsevec
|
||||
--------------------------
|
||||
{1:1.5e+38,2:-1.5e+38}/2
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1.5e+38,2:-1.5e+38}/2'::sparsevec;
|
||||
sparsevec
|
||||
--------------------------
|
||||
{1:1.5e+38,2:-1.5e+38}/2
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1.5e-38,2:-1.5e-38}/2'::sparsevec;
|
||||
sparsevec
|
||||
--------------------------
|
||||
{1:1.5e-38,2:-1.5e-38}/2
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:4e38,2:1}/2'::sparsevec;
|
||||
ERROR: "4e38" is out of range for type sparsevec
|
||||
LINE 1: SELECT '{1:4e38,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:-4e38,2:1}/2'::sparsevec;
|
||||
ERROR: "-4e38" is out of range for type sparsevec
|
||||
LINE 1: SELECT '{1:-4e38,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1e-46,2:1}/2'::sparsevec;
|
||||
ERROR: "1e-46" is out of range for type sparsevec
|
||||
LINE 1: SELECT '{1:1e-46,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:-1e-46,2:1}/2'::sparsevec;
|
||||
ERROR: "-1e-46" is out of range for type sparsevec
|
||||
LINE 1: SELECT '{1:-1e-46,2:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT ''::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: ""
|
||||
LINE 1: SELECT ''::sparsevec;
|
||||
^
|
||||
DETAIL: Vector contents must start with "{".
|
||||
SELECT '{'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{"
|
||||
LINE 1: SELECT '{'::sparsevec;
|
||||
^
|
||||
SELECT '{ '::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{ "
|
||||
LINE 1: SELECT '{ '::sparsevec;
|
||||
^
|
||||
SELECT '{:'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{:"
|
||||
LINE 1: SELECT '{:'::sparsevec;
|
||||
^
|
||||
SELECT '{,'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{,"
|
||||
LINE 1: SELECT '{,'::sparsevec;
|
||||
^
|
||||
SELECT '{}'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{}"
|
||||
LINE 1: SELECT '{}'::sparsevec;
|
||||
^
|
||||
DETAIL: Unexpected end of input.
|
||||
SELECT '{}/'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{}/"
|
||||
LINE 1: SELECT '{}/'::sparsevec;
|
||||
^
|
||||
SELECT '{}/1'::sparsevec;
|
||||
sparsevec
|
||||
-----------
|
||||
{}/1
|
||||
(1 row)
|
||||
|
||||
SELECT '{}/1a'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{}/1a"
|
||||
LINE 1: SELECT '{}/1a'::sparsevec;
|
||||
^
|
||||
DETAIL: Junk after closing.
|
||||
SELECT '{ }/1'::sparsevec;
|
||||
sparsevec
|
||||
-----------
|
||||
{}/1
|
||||
(1 row)
|
||||
|
||||
SELECT '{:}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{:}/1"
|
||||
LINE 1: SELECT '{:}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{,}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{,}/1"
|
||||
LINE 1: SELECT '{,}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1,}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1,}/1"
|
||||
LINE 1: SELECT '{1,}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{:1}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{:1}/1"
|
||||
LINE 1: SELECT '{:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1:}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1:}/1"
|
||||
LINE 1: SELECT '{1:}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1a:1}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1a:1}/1"
|
||||
LINE 1: SELECT '{1a:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1a}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1:1a}/1"
|
||||
LINE 1: SELECT '{1:1a}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1,}/1'::sparsevec;
|
||||
ERROR: invalid input syntax for type sparsevec: "{1:1,}/1"
|
||||
LINE 1: SELECT '{1:1,}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{1:0,2:1,3:0}/3'::sparsevec;
|
||||
sparsevec
|
||||
-----------
|
||||
{2:1}/3
|
||||
(1 row)
|
||||
|
||||
SELECT '{2:1,1:1}/2'::sparsevec;
|
||||
sparsevec
|
||||
-------------
|
||||
{1:1,2:1}/2
|
||||
(1 row)
|
||||
|
||||
SELECT '{1:1,1:1}/2'::sparsevec;
|
||||
ERROR: indexes must not contain duplicates
|
||||
LINE 1: SELECT '{1:1,1:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{1:1,2:1,1:1}/2'::sparsevec;
|
||||
ERROR: indexes must not contain duplicates
|
||||
LINE 1: SELECT '{1:1,2:1,1:1}/2'::sparsevec;
|
||||
^
|
||||
SELECT '{}/5'::sparsevec;
|
||||
sparsevec
|
||||
-----------
|
||||
{}/5
|
||||
(1 row)
|
||||
|
||||
SELECT '{}/-1'::sparsevec;
|
||||
ERROR: sparsevec must have at least 1 dimension
|
||||
LINE 1: SELECT '{}/-1'::sparsevec;
|
||||
^
|
||||
SELECT '{}/100001'::sparsevec;
|
||||
ERROR: sparsevec cannot have more than 100000 dimensions
|
||||
LINE 1: SELECT '{}/100001'::sparsevec;
|
||||
^
|
||||
SELECT '{0:1}/1'::sparsevec;
|
||||
ERROR: index "0" is out of range for type sparsevec
|
||||
LINE 1: SELECT '{0:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{2:1}/1'::sparsevec;
|
||||
ERROR: index must be less than or equal to dimensions
|
||||
LINE 1: SELECT '{2:1}/1'::sparsevec;
|
||||
^
|
||||
SELECT '{}/3'::sparsevec(3);
|
||||
sparsevec
|
||||
-----------
|
||||
{}/3
|
||||
(1 row)
|
||||
|
||||
SELECT '{}/3'::sparsevec(2);
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
SELECT '{}/3'::sparsevec(3, 2);
|
||||
ERROR: invalid type modifier
|
||||
LINE 1: SELECT '{}/3'::sparsevec(3, 2);
|
||||
^
|
||||
SELECT '{}/3'::sparsevec('a');
|
||||
ERROR: invalid input syntax for type integer: "a"
|
||||
LINE 1: SELECT '{}/3'::sparsevec('a');
|
||||
^
|
||||
SELECT '{}/3'::sparsevec(0);
|
||||
ERROR: dimensions for type sparsevec must be at least 1
|
||||
LINE 1: SELECT '{}/3'::sparsevec(0);
|
||||
^
|
||||
SELECT '{}/3'::sparsevec(100001);
|
||||
ERROR: dimensions for type sparsevec cannot exceed 100000
|
||||
LINE 1: SELECT '{}/3'::sparsevec(100001);
|
||||
^
|
||||
@@ -1,176 +0,0 @@
|
||||
SELECT '[1,2,3]'::vector;
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[-1,-2,-3]'::vector;
|
||||
vector
|
||||
------------
|
||||
[-1,-2,-3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.,2.,3.]'::vector;
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT ' [ 1, 2 , 3 ] '::vector;
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.23456]'::vector;
|
||||
vector
|
||||
-----------
|
||||
[1.23456]
|
||||
(1 row)
|
||||
|
||||
SELECT '[hello,1]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[hello,1]"
|
||||
LINE 1: SELECT '[hello,1]'::vector;
|
||||
^
|
||||
SELECT '[NaN,1]'::vector;
|
||||
ERROR: NaN not allowed in vector
|
||||
LINE 1: SELECT '[NaN,1]'::vector;
|
||||
^
|
||||
SELECT '[Infinity,1]'::vector;
|
||||
ERROR: infinite value not allowed in vector
|
||||
LINE 1: SELECT '[Infinity,1]'::vector;
|
||||
^
|
||||
SELECT '[-Infinity,1]'::vector;
|
||||
ERROR: infinite value not allowed in vector
|
||||
LINE 1: SELECT '[-Infinity,1]'::vector;
|
||||
^
|
||||
SELECT '[1.5e38,-1.5e38]'::vector;
|
||||
vector
|
||||
--------------------
|
||||
[1.5e+38,-1.5e+38]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.5e+38,-1.5e+38]'::vector;
|
||||
vector
|
||||
--------------------
|
||||
[1.5e+38,-1.5e+38]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.5e-38,-1.5e-38]'::vector;
|
||||
vector
|
||||
--------------------
|
||||
[1.5e-38,-1.5e-38]
|
||||
(1 row)
|
||||
|
||||
SELECT '[4e38,1]'::vector;
|
||||
ERROR: "4e38" is out of range for type vector
|
||||
LINE 1: SELECT '[4e38,1]'::vector;
|
||||
^
|
||||
SELECT '[-4e38,1]'::vector;
|
||||
ERROR: "-4e38" is out of range for type vector
|
||||
LINE 1: SELECT '[-4e38,1]'::vector;
|
||||
^
|
||||
SELECT '[1e-46,1]'::vector;
|
||||
vector
|
||||
--------
|
||||
[0,1]
|
||||
(1 row)
|
||||
|
||||
SELECT '[-1e-46,1]'::vector;
|
||||
vector
|
||||
--------
|
||||
[-0,1]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1,2,3'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1,2,3"
|
||||
LINE 1: SELECT '[1,2,3'::vector;
|
||||
^
|
||||
SELECT '[1,2,3]9'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1,2,3]9"
|
||||
LINE 1: SELECT '[1,2,3]9'::vector;
|
||||
^
|
||||
DETAIL: Junk after closing right brace.
|
||||
SELECT '1,2,3'::vector;
|
||||
ERROR: invalid input syntax for type vector: "1,2,3"
|
||||
LINE 1: SELECT '1,2,3'::vector;
|
||||
^
|
||||
DETAIL: Vector contents must start with "[".
|
||||
SELECT ''::vector;
|
||||
ERROR: invalid input syntax for type vector: ""
|
||||
LINE 1: SELECT ''::vector;
|
||||
^
|
||||
DETAIL: Vector contents must start with "[".
|
||||
SELECT '['::vector;
|
||||
ERROR: invalid input syntax for type vector: "["
|
||||
LINE 1: SELECT '['::vector;
|
||||
^
|
||||
SELECT '[ '::vector;
|
||||
ERROR: invalid input syntax for type vector: "[ "
|
||||
LINE 1: SELECT '[ '::vector;
|
||||
^
|
||||
SELECT '[,'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[,"
|
||||
LINE 1: SELECT '[,'::vector;
|
||||
^
|
||||
SELECT '[]'::vector;
|
||||
ERROR: vector must have at least 1 dimension
|
||||
LINE 1: SELECT '[]'::vector;
|
||||
^
|
||||
SELECT '[ ]'::vector;
|
||||
ERROR: vector must have at least 1 dimension
|
||||
LINE 1: SELECT '[ ]'::vector;
|
||||
^
|
||||
SELECT '[,]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[,]"
|
||||
LINE 1: SELECT '[,]'::vector;
|
||||
^
|
||||
SELECT '[1,]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1,]"
|
||||
LINE 1: SELECT '[1,]'::vector;
|
||||
^
|
||||
SELECT '[1a]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1a]"
|
||||
LINE 1: SELECT '[1a]'::vector;
|
||||
^
|
||||
SELECT '[1,,3]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1,,3]"
|
||||
LINE 1: SELECT '[1,,3]'::vector;
|
||||
^
|
||||
SELECT '[1, ,3]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1, ,3]"
|
||||
LINE 1: SELECT '[1, ,3]'::vector;
|
||||
^
|
||||
SELECT '[1,2,3]'::vector(3);
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1,2,3]'::vector(2);
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
SELECT '[1,2,3]'::vector(3, 2);
|
||||
ERROR: invalid type modifier
|
||||
LINE 1: SELECT '[1,2,3]'::vector(3, 2);
|
||||
^
|
||||
SELECT '[1,2,3]'::vector('a');
|
||||
ERROR: invalid input syntax for type integer: "a"
|
||||
LINE 1: SELECT '[1,2,3]'::vector('a');
|
||||
^
|
||||
SELECT '[1,2,3]'::vector(0);
|
||||
ERROR: dimensions for type vector must be at least 1
|
||||
LINE 1: SELECT '[1,2,3]'::vector(0);
|
||||
^
|
||||
SELECT '[1,2,3]'::vector(16001);
|
||||
ERROR: dimensions for type vector cannot exceed 16000
|
||||
LINE 1: SELECT '[1,2,3]'::vector(16001);
|
||||
^
|
||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);
|
||||
unnest
|
||||
---------
|
||||
[1,2,3]
|
||||
[4,5,6]
|
||||
(2 rows)
|
||||
|
||||
SELECT '{"[1,2,3]"}'::vector(2)[];
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
@@ -1,3 +1,179 @@
|
||||
SELECT '[1,2,3]'::vector;
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[-1,-2,-3]'::vector;
|
||||
vector
|
||||
------------
|
||||
[-1,-2,-3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.,2.,3.]'::vector;
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT ' [ 1, 2 , 3 ] '::vector;
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.23456]'::vector;
|
||||
vector
|
||||
-----------
|
||||
[1.23456]
|
||||
(1 row)
|
||||
|
||||
SELECT '[hello,1]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[hello,1]"
|
||||
LINE 1: SELECT '[hello,1]'::vector;
|
||||
^
|
||||
SELECT '[NaN,1]'::vector;
|
||||
ERROR: NaN not allowed in vector
|
||||
LINE 1: SELECT '[NaN,1]'::vector;
|
||||
^
|
||||
SELECT '[Infinity,1]'::vector;
|
||||
ERROR: infinite value not allowed in vector
|
||||
LINE 1: SELECT '[Infinity,1]'::vector;
|
||||
^
|
||||
SELECT '[-Infinity,1]'::vector;
|
||||
ERROR: infinite value not allowed in vector
|
||||
LINE 1: SELECT '[-Infinity,1]'::vector;
|
||||
^
|
||||
SELECT '[1.5e38,-1.5e38]'::vector;
|
||||
vector
|
||||
--------------------
|
||||
[1.5e+38,-1.5e+38]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.5e+38,-1.5e+38]'::vector;
|
||||
vector
|
||||
--------------------
|
||||
[1.5e+38,-1.5e+38]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1.5e-38,-1.5e-38]'::vector;
|
||||
vector
|
||||
--------------------
|
||||
[1.5e-38,-1.5e-38]
|
||||
(1 row)
|
||||
|
||||
SELECT '[4e38,1]'::vector;
|
||||
ERROR: "4e38" is out of range for type vector
|
||||
LINE 1: SELECT '[4e38,1]'::vector;
|
||||
^
|
||||
SELECT '[-4e38,1]'::vector;
|
||||
ERROR: "-4e38" is out of range for type vector
|
||||
LINE 1: SELECT '[-4e38,1]'::vector;
|
||||
^
|
||||
SELECT '[1e-46,1]'::vector;
|
||||
vector
|
||||
--------
|
||||
[0,1]
|
||||
(1 row)
|
||||
|
||||
SELECT '[-1e-46,1]'::vector;
|
||||
vector
|
||||
--------
|
||||
[-0,1]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1,2,3'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1,2,3"
|
||||
LINE 1: SELECT '[1,2,3'::vector;
|
||||
^
|
||||
SELECT '[1,2,3]9'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1,2,3]9"
|
||||
LINE 1: SELECT '[1,2,3]9'::vector;
|
||||
^
|
||||
DETAIL: Junk after closing right brace.
|
||||
SELECT '1,2,3'::vector;
|
||||
ERROR: invalid input syntax for type vector: "1,2,3"
|
||||
LINE 1: SELECT '1,2,3'::vector;
|
||||
^
|
||||
DETAIL: Vector contents must start with "[".
|
||||
SELECT ''::vector;
|
||||
ERROR: invalid input syntax for type vector: ""
|
||||
LINE 1: SELECT ''::vector;
|
||||
^
|
||||
DETAIL: Vector contents must start with "[".
|
||||
SELECT '['::vector;
|
||||
ERROR: invalid input syntax for type vector: "["
|
||||
LINE 1: SELECT '['::vector;
|
||||
^
|
||||
SELECT '[ '::vector;
|
||||
ERROR: invalid input syntax for type vector: "[ "
|
||||
LINE 1: SELECT '[ '::vector;
|
||||
^
|
||||
SELECT '[,'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[,"
|
||||
LINE 1: SELECT '[,'::vector;
|
||||
^
|
||||
SELECT '[]'::vector;
|
||||
ERROR: vector must have at least 1 dimension
|
||||
LINE 1: SELECT '[]'::vector;
|
||||
^
|
||||
SELECT '[ ]'::vector;
|
||||
ERROR: vector must have at least 1 dimension
|
||||
LINE 1: SELECT '[ ]'::vector;
|
||||
^
|
||||
SELECT '[,]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[,]"
|
||||
LINE 1: SELECT '[,]'::vector;
|
||||
^
|
||||
SELECT '[1,]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1,]"
|
||||
LINE 1: SELECT '[1,]'::vector;
|
||||
^
|
||||
SELECT '[1a]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1a]"
|
||||
LINE 1: SELECT '[1a]'::vector;
|
||||
^
|
||||
SELECT '[1,,3]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1,,3]"
|
||||
LINE 1: SELECT '[1,,3]'::vector;
|
||||
^
|
||||
SELECT '[1, ,3]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1, ,3]"
|
||||
LINE 1: SELECT '[1, ,3]'::vector;
|
||||
^
|
||||
SELECT '[1,2,3]'::vector(3);
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1,2,3]'::vector(2);
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
SELECT '[1,2,3]'::vector(3, 2);
|
||||
ERROR: invalid type modifier
|
||||
LINE 1: SELECT '[1,2,3]'::vector(3, 2);
|
||||
^
|
||||
SELECT '[1,2,3]'::vector('a');
|
||||
ERROR: invalid input syntax for type integer: "a"
|
||||
LINE 1: SELECT '[1,2,3]'::vector('a');
|
||||
^
|
||||
SELECT '[1,2,3]'::vector(0);
|
||||
ERROR: dimensions for type vector must be at least 1
|
||||
LINE 1: SELECT '[1,2,3]'::vector(0);
|
||||
^
|
||||
SELECT '[1,2,3]'::vector(16001);
|
||||
ERROR: dimensions for type vector cannot exceed 16000
|
||||
LINE 1: SELECT '[1,2,3]'::vector(16001);
|
||||
^
|
||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);
|
||||
unnest
|
||||
---------
|
||||
[1,2,3]
|
||||
[4,5,6]
|
||||
(2 rows)
|
||||
|
||||
SELECT '{"[1,2,3]"}'::vector(2)[];
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
SELECT '[1,2,3]'::vector + '[4,5,6]';
|
||||
?column?
|
||||
----------
|
||||
@@ -182,6 +358,18 @@ SELECT vector_norm('[3e37,4e37]')::real;
|
||||
5e+37
|
||||
(1 row)
|
||||
|
||||
SELECT vector_norm('[0,0]');
|
||||
vector_norm
|
||||
-------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT vector_norm('[2]');
|
||||
vector_norm
|
||||
-------------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT l2_distance('[0,0]'::vector, '[3,4]');
|
||||
l2_distance
|
||||
-------------
|
||||
@@ -418,6 +606,20 @@ SELECT subvector('[1,2,3,4,5]'::vector, 3, -1);
|
||||
ERROR: vector must have at least 1 dimension
|
||||
SELECT subvector('[1,2,3,4,5]'::vector, -1, 2);
|
||||
ERROR: vector must have at least 1 dimension
|
||||
SELECT subvector('[1,2,3,4,5]'::vector, 2147483647, 10);
|
||||
ERROR: vector must have at least 1 dimension
|
||||
SELECT subvector('[1,2,3,4,5]'::vector, 3, 2147483647);
|
||||
subvector
|
||||
-----------
|
||||
[3,4,5]
|
||||
(1 row)
|
||||
|
||||
SELECT subvector('[1,2,3,4,5]'::vector, -2147483644, 2147483647);
|
||||
subvector
|
||||
-----------
|
||||
[1,2]
|
||||
(1 row)
|
||||
|
||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]']) v;
|
||||
avg
|
||||
-----------
|
||||
11
test/perl/PostgreSQL/Test/Cluster.pm
Normal file
11
test/perl/PostgreSQL/Test/Cluster.pm
Normal file
@@ -0,0 +1,11 @@
|
||||
package PostgreSQL::Test::Cluster;
|
||||
|
||||
use PostgresNode;
|
||||
|
||||
sub new
|
||||
{
|
||||
my ($class, $name) = @_;
|
||||
return get_new_node($name);
|
||||
}
|
||||
|
||||
1;
|
||||
5
test/perl/PostgreSQL/Test/Utils.pm
Normal file
5
test/perl/PostgreSQL/Test/Utils.pm
Normal file
@@ -0,0 +1,5 @@
|
||||
package PostgreSQL::Test::Utils;
|
||||
|
||||
use TestLib;
|
||||
|
||||
1;
|
||||
@@ -1,8 +0,0 @@
|
||||
use PostgreSQL::Test::Cluster;
|
||||
|
||||
sub get_new_node
|
||||
{
|
||||
return PostgreSQL::Test::Cluster->new(@_);
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,3 +0,0 @@
|
||||
use PostgreSQL::Test::Utils;
|
||||
|
||||
1;
|
||||
34
test/sql/btree.sql
Normal file
34
test/sql/btree.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
-- vector
|
||||
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
|
||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||
SELECT * FROM t ORDER BY val;
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
-- halfvec
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
|
||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||
SELECT * FROM t ORDER BY val;
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
-- sparsevec
|
||||
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
|
||||
SELECT * FROM t WHERE val = '{1:1,2:2,3:3}/3';
|
||||
SELECT * FROM t ORDER BY val;
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,10 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
|
||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||
SELECT * FROM t ORDER BY val;
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,10 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
|
||||
SELECT * FROM t WHERE val = '{1:1,2:2,3:3}/3';
|
||||
SELECT * FROM t ORDER BY val;
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,10 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t (val);
|
||||
|
||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||
SELECT * FROM t ORDER BY val;
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,10 +1,42 @@
|
||||
CREATE TABLE t (val vector(3), val2 halfvec(3), val3 sparsevec(3));
|
||||
INSERT INTO t (val, val2, val3) VALUES ('[0,0,0]', '[0,0,0]', '{}/3'), ('[1,2,3]', '[1,2,3]', '{1:1,2:2,3:3}/3'), ('[1,1,1]', '[1,1,1]', '{1:1,2:1,3:1}/3'), (NULL, NULL, NULL);
|
||||
-- vector
|
||||
|
||||
CREATE TABLE t2 (val vector(3), val2 halfvec(3), val3 sparsevec(3));
|
||||
CREATE TABLE t (val vector(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
|
||||
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
||||
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
||||
CREATE TABLE t2 (val vector(3));
|
||||
|
||||
\copy t TO 'results/vector.bin' WITH (FORMAT binary)
|
||||
\copy t2 FROM 'results/vector.bin' WITH (FORMAT binary)
|
||||
|
||||
SELECT * FROM t2 ORDER BY val;
|
||||
|
||||
DROP TABLE t;
|
||||
DROP TABLE t2;
|
||||
|
||||
-- halfvec
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
|
||||
CREATE TABLE t2 (val halfvec(3));
|
||||
|
||||
\copy t TO 'results/halfvec.bin' WITH (FORMAT binary)
|
||||
\copy t2 FROM 'results/halfvec.bin' WITH (FORMAT binary)
|
||||
|
||||
SELECT * FROM t2 ORDER BY val;
|
||||
|
||||
DROP TABLE t;
|
||||
DROP TABLE t2;
|
||||
|
||||
-- sparsevec
|
||||
|
||||
CREATE TABLE t (val sparsevec(3));
|
||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||
|
||||
CREATE TABLE t2 (val sparsevec(3));
|
||||
|
||||
\copy t TO 'results/sparsevec.bin' WITH (FORMAT binary)
|
||||
\copy t2 FROM 'results/sparsevec.bin' WITH (FORMAT binary)
|
||||
|
||||
SELECT * FROM t2 ORDER BY val;
|
||||
|
||||
|
||||
@@ -1,3 +1,42 @@
|
||||
SELECT '[1,2,3]'::halfvec;
|
||||
SELECT '[-1,-2,-3]'::halfvec;
|
||||
SELECT '[1.,2.,3.]'::halfvec;
|
||||
SELECT ' [ 1, 2 , 3 ] '::halfvec;
|
||||
SELECT '[1.23456]'::halfvec;
|
||||
SELECT '[hello,1]'::halfvec;
|
||||
SELECT '[NaN,1]'::halfvec;
|
||||
SELECT '[Infinity,1]'::halfvec;
|
||||
SELECT '[-Infinity,1]'::halfvec;
|
||||
SELECT '[65519,-65519]'::halfvec;
|
||||
SELECT '[65520,-65520]'::halfvec;
|
||||
SELECT '[1e-8,-1e-8]'::halfvec;
|
||||
SELECT '[4e38,1]'::halfvec;
|
||||
SELECT '[1e-46,1]'::halfvec;
|
||||
SELECT '[1,2,3'::halfvec;
|
||||
SELECT '[1,2,3]9'::halfvec;
|
||||
SELECT '1,2,3'::halfvec;
|
||||
SELECT ''::halfvec;
|
||||
SELECT '['::halfvec;
|
||||
SELECT '[ '::halfvec;
|
||||
SELECT '[,'::halfvec;
|
||||
SELECT '[]'::halfvec;
|
||||
SELECT '[ ]'::halfvec;
|
||||
SELECT '[,]'::halfvec;
|
||||
SELECT '[1,]'::halfvec;
|
||||
SELECT '[1a]'::halfvec;
|
||||
SELECT '[1,,3]'::halfvec;
|
||||
SELECT '[1, ,3]'::halfvec;
|
||||
|
||||
SELECT '[1,2,3]'::halfvec(3);
|
||||
SELECT '[1,2,3]'::halfvec(2);
|
||||
SELECT '[1,2,3]'::halfvec(3, 2);
|
||||
SELECT '[1,2,3]'::halfvec('a');
|
||||
SELECT '[1,2,3]'::halfvec(0);
|
||||
SELECT '[1,2,3]'::halfvec(16001);
|
||||
|
||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::halfvec[]);
|
||||
SELECT '{"[1,2,3]"}'::halfvec(2)[];
|
||||
|
||||
SELECT '[1,2,3]'::halfvec + '[4,5,6]';
|
||||
SELECT '[65519]'::halfvec + '[65519]';
|
||||
SELECT '[1,2]'::halfvec + '[3]';
|
||||
@@ -40,6 +79,8 @@ SELECT vector_dims('[1,2,3]'::halfvec);
|
||||
SELECT round(l2_norm('[1,1]'::halfvec)::numeric, 5);
|
||||
SELECT l2_norm('[3,4]'::halfvec);
|
||||
SELECT l2_norm('[0,1]'::halfvec);
|
||||
SELECT l2_norm('[0,0]'::halfvec);
|
||||
SELECT l2_norm('[2]'::halfvec);
|
||||
|
||||
SELECT l2_distance('[0,0]'::halfvec, '[3,4]');
|
||||
SELECT l2_distance('[0,0]'::halfvec, '[0,1]');
|
||||
@@ -88,6 +129,9 @@ SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 9);
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, 1, 0);
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, -1);
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, -1, 2);
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, 2147483647, 10);
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 2147483647);
|
||||
SELECT subvector('[1,2,3,4,5]'::halfvec, -2147483644, 2147483647);
|
||||
|
||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]']) v;
|
||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]', NULL]) v;
|
||||
@@ -1,38 +0,0 @@
|
||||
SELECT '[1,2,3]'::halfvec;
|
||||
SELECT '[-1,-2,-3]'::halfvec;
|
||||
SELECT '[1.,2.,3.]'::halfvec;
|
||||
SELECT ' [ 1, 2 , 3 ] '::halfvec;
|
||||
SELECT '[1.23456]'::halfvec;
|
||||
SELECT '[hello,1]'::halfvec;
|
||||
SELECT '[NaN,1]'::halfvec;
|
||||
SELECT '[Infinity,1]'::halfvec;
|
||||
SELECT '[-Infinity,1]'::halfvec;
|
||||
SELECT '[65519,-65519]'::halfvec;
|
||||
SELECT '[65520,-65520]'::halfvec;
|
||||
SELECT '[1e-8,-1e-8]'::halfvec;
|
||||
SELECT '[4e38,1]'::halfvec;
|
||||
SELECT '[1e-46,1]'::halfvec;
|
||||
SELECT '[1,2,3'::halfvec;
|
||||
SELECT '[1,2,3]9'::halfvec;
|
||||
SELECT '1,2,3'::halfvec;
|
||||
SELECT ''::halfvec;
|
||||
SELECT '['::halfvec;
|
||||
SELECT '[ '::halfvec;
|
||||
SELECT '[,'::halfvec;
|
||||
SELECT '[]'::halfvec;
|
||||
SELECT '[ ]'::halfvec;
|
||||
SELECT '[,]'::halfvec;
|
||||
SELECT '[1,]'::halfvec;
|
||||
SELECT '[1a]'::halfvec;
|
||||
SELECT '[1,,3]'::halfvec;
|
||||
SELECT '[1, ,3]'::halfvec;
|
||||
|
||||
SELECT '[1,2,3]'::halfvec(3);
|
||||
SELECT '[1,2,3]'::halfvec(2);
|
||||
SELECT '[1,2,3]'::halfvec(3, 2);
|
||||
SELECT '[1,2,3]'::halfvec('a');
|
||||
SELECT '[1,2,3]'::halfvec(0);
|
||||
SELECT '[1,2,3]'::halfvec(16001);
|
||||
|
||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::halfvec[]);
|
||||
SELECT '{"[1,2,3]"}'::halfvec(2)[];
|
||||
@@ -1,5 +1,7 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
-- hamming
|
||||
|
||||
CREATE TABLE t (val bit(3));
|
||||
INSERT INTO t (val) VALUES (B'000'), (B'100'), (B'111'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
||||
@@ -11,7 +13,21 @@ SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <~> (SELECT NULL::bit)) t2;
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
-- TODO move
|
||||
-- jaccard
|
||||
|
||||
CREATE TABLE t (val bit(4));
|
||||
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val bit_jaccard_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES (B'1110');
|
||||
|
||||
SELECT * FROM t ORDER BY val <%> B'1111';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
-- varbit
|
||||
|
||||
CREATE TABLE t (val varbit(3));
|
||||
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
||||
CREATE INDEX ON t USING hnsw ((val::bit(3)) bit_hamming_ops);
|
||||
@@ -1,12 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
CREATE TABLE t (val bit(4));
|
||||
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val bit_jaccard_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES (B'1110');
|
||||
|
||||
SELECT * FROM t ORDER BY val <%> B'1111';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
|
||||
|
||||
DROP TABLE t;
|
||||
58
test/sql/hnsw_halfvec.sql
Normal file
58
test/sql/hnsw_halfvec.sql
Normal file
@@ -0,0 +1,58 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
-- L2
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_l2_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
||||
SELECT COUNT(*) FROM t;
|
||||
|
||||
TRUNCATE t;
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
-- inner product
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_ip_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
-- cosine
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_cosine_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
-- L1
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_l1_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
|
||||
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::halfvec)) t2;
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,13 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_cosine_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
|
||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,12 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_ip_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
|
||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,12 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_l1_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
|
||||
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::halfvec)) t2;
|
||||
|
||||
DROP TABLE t;
|
||||
@@ -1,16 +0,0 @@
|
||||
SET enable_seqscan = off;
|
||||
|
||||
CREATE TABLE t (val halfvec(3));
|
||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||
CREATE INDEX ON t USING hnsw (val halfvec_l2_ops);
|
||||
|
||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
||||
SELECT COUNT(*) FROM t;
|
||||
|
||||
TRUNCATE t;
|
||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||
|
||||
DROP TABLE t;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user