Compare commits

..

111 Commits

Author SHA1 Message Date
Andrew Kane
3172d7ef57 Moved type support to support functions [skip ci] 2024-04-23 12:51:04 -07:00
Heikki Linnakangas
d1b83991af Forbid zero values in sparsevec's binary input function (#528)
The text input function simply left out any zero values, but the
binary input function did not. That's problematic because you end up
with an "unnormalized" sparse vector, which behaves in weird ways.  At
least sparsevec_cmp_internal() expects both inputs to not contain
zeros.

The binary send function never produces such zero values, but an
external tool could. Or to test, you can use COPY TO (FORMAT BINARY),
use a hex editor to edit one of the values to be zero, and copy it
back with COPY FROM (FORMAT BINARY).
2024-04-23 09:13:53 -07:00
Andrew Kane
6c247a38d3 Updated readme [skip ci] 2024-04-22 21:56:05 -07:00
Andrew Kane
6639cde19d Updated readme [skip ci] 2024-04-22 21:32:59 -07:00
Andrew Kane
bd409f0c6a Moved HnswGetType call [skip ci] 2024-04-22 19:22:09 -07:00
Andrew Kane
1994fd003a Removed unneeded headers [skip ci] 2024-04-22 19:10:50 -07:00
Andrew Kane
bd62561a19 Added support function for l2_normalize to ivfflat 2024-04-22 19:06:06 -07:00
Andrew Kane
f14c21748b Added support function for l2_normalize [skip ci] 2024-04-22 18:36:47 -07:00
Andrew Kane
2b77005610 Removed type-specific code from ivfscan 2024-04-22 18:12:18 -07:00
Andrew Kane
e884b3aa69 Added opclasses to readme [skip ci] 2024-04-22 16:29:34 -07:00
Andrew Kane
ab71c12a28 Added comments on dispatching [skip ci] 2024-04-22 16:18:57 -07:00
Andrew Kane
1804c63e27 Added more tests for vector distance functions [skip ci] 2024-04-22 15:53:13 -07:00
Andrew Kane
4e6aa2f0c1 Added DISABLE_DISPATCH option [skip ci] 2024-04-22 15:43:07 -07:00
Andrew Kane
40e86251c3 Added VECTOR_TARGET_CLONES to VectorL1Distance [skip ci] 2024-04-22 15:15:57 -07:00
Andrew Kane
0c9ae4b187 Added CPU dispatching for L1 distance for halfvec 2024-04-22 15:02:17 -07:00
Andrew Kane
d83af48e70 Improved tests for halfvec l1_distance [skip ci] 2024-04-22 14:43:54 -07:00
Andrew Kane
b2f7dad8a7 Removed support for L1 distance and Jaccard distance from ivfflat due to non-optimal clustering 2024-04-22 14:11:29 -07:00
Andrew Kane
881fbc15ef Added L1 distance operator to docs [skip ci] 2024-04-22 13:22:28 -07:00
Andrew Kane
f9941c2992 Moved L1 distance to halfutils [skip ci] 2024-04-22 13:19:42 -07:00
Andrew Kane
f9c071a761 Improved tests for L1 distance with halfvec 2024-04-22 13:14:45 -07:00
Andrew Kane
9f4b770db3 Added support for indexing sparsevec with L1 distance [skip ci] 2024-04-22 13:08:12 -07:00
Andrew Kane
70b299a7ff Added support for indexing halfvec with L1 distance [skip ci] 2024-04-22 13:00:59 -07:00
Andrew Kane
d46d014234 Updated test name [skip ci] 2024-04-22 12:57:01 -07:00
Andrew Kane
655adc535a Added bit examples for ivfflat [skip ci] 2024-04-22 12:51:38 -07:00
Andrew Kane
47f0a5e9ac Updated test name [skip ci] 2024-04-22 12:48:22 -07:00
Andrew Kane
af9d50481d Added support for indexing L1 distance 2024-04-22 12:44:03 -07:00
Andrew Kane
6dad8460a6 Updated readme [skip ci] 2024-04-22 10:45:41 -07:00
Andrew Kane
ed4837cc4f Renamed bit files 2024-04-22 10:22:18 -07:00
Andrew Kane
3df5655b30 Removed unneeded headers [skip ci] 2024-04-19 18:09:38 -07:00
Andrew Kane
e4c4ac9648 Added more tests for sparsevec to halfvec cast [skip ci] 2024-04-19 18:05:03 -07:00
Andrew Kane
fb77671d05 Added casts between halfvec and sparsevec 2024-04-19 18:03:07 -07:00
Andrew Kane
fd4fbd238c Updated sparsevec input to support indices in any order [skip ci] 2024-04-19 16:54:19 -07:00
Andrew Kane
4e093f95be Improved consistency of CPU dispatching code 2024-04-18 14:13:12 -07:00
Andrew Kane
fb3c964ac2 Improved performance of bit distance functions - #519
Co-authored-by: Nathan Bossart <nathan@postgresql.org>
Co-authored-by: "Jonathan S. Katz" <jkatz@users.noreply.github.com>
2024-04-18 13:45:00 -07:00
Andrew Kane
0b938f8328 Improved halfvec dispatching code [skip ci] 2024-04-17 20:22:14 -07:00
Andrew Kane
6153e173f3 Updated comments [skip ci] 2024-04-17 18:57:59 -07:00
Andrew Kane
eb48b9eec4 Added comments [skip ci] 2024-04-17 18:53:58 -07:00
Andrew Kane
fb6af03361 Fixed CPU dispatching check for halfvec distance functions 2024-04-17 18:27:27 -07:00
Andrew Kane
e2d8907180 Added todo [skip ci] 2024-04-17 17:25:53 -07:00
Andrew Kane
09ea1b0d5c Improved target_clones logic [skip ci] 2024-04-17 15:05:47 -07:00
Andrew Kane
8d68f88507 Improved target_clones logic [skip ci] 2024-04-17 15:05:19 -07:00
Andrew Kane
301c8083f5 Added check for undefined behavior to CI [skip ci] 2024-04-17 02:03:46 -07:00
Andrew Kane
8b33a359ce Updated VECTOR_ARRAY_SIZE for alignment [skip ci] 2024-04-17 00:55:28 -07:00
Andrew Kane
77ec24641e Fixed flaky test [skip ci] 2024-04-17 00:27:37 -07:00
Andrew Kane
576a37e975 Ensure items are always aligned 2024-04-17 00:17:40 -07:00
Andrew Kane
cf570810eb Fixed alignment for bit in IVFFlat - fixes #520 2024-04-17 00:05:31 -07:00
Andrew Kane
c361f80465 Synced recall test [skip ci] 2024-04-16 17:27:06 -07:00
Andrew Kane
e730fef99d Fixed flaky test [skip ci] 2024-04-16 17:25:50 -07:00
Andrew Kane
4e2b76e627 Skip duplicate center check for bit [skip ci] 2024-04-16 17:20:48 -07:00
Andrew Kane
04af15c9d6 Added support for bit to IVFFlat 2024-04-16 17:12:27 -07:00
Andrew Kane
819b6cf312 Added comments to vector.sql [skip ci] 2024-04-16 15:47:25 -07:00
Andrew Kane
31dfd3d1a6 Added type to assertion message [skip ci] 2024-04-16 13:11:37 -07:00
Andrew Kane
8df8dd01b9 Added halfvec to distance functions TAP test 2024-04-16 13:09:44 -07:00
Andrew Kane
b9b30cc16e Added function name to assertion message [skip ci] 2024-04-16 12:20:22 -07:00
Andrew Kane
e1565af0dc Added TAP test for sparsevec distance functions 2024-04-16 12:11:54 -07:00
Andrew Kane
26d6fbb1d0 Updated comment [skip ci] 2024-04-16 11:45:19 -07:00
Andrew Kane
6c61b5ce6f Updated comment [skip ci] 2024-04-16 11:45:03 -07:00
Andrew Kane
588873e145 Added comment [skip ci] 2024-04-16 11:38:59 -07:00
Andrew Kane
7adb8a6e0e Fixed sparsevec comparison function and added test for consistency 2024-04-16 11:36:45 -07:00
Andrew Kane
5b0eff9dae Updated comment [skip ci] 2024-04-15 15:53:07 -07:00
Andrew Kane
5cf75c0b83 Added comment [skip ci] 2024-04-15 15:50:59 -07:00
Andrew Kane
5215c28923 Moved norm check to separate function 2024-04-15 15:32:08 -07:00
Andrew Kane
342d82be65 Improved l2_normalize test for sparsevec [skip ci] 2024-04-15 14:59:11 -07:00
Andrew Kane
bf3ef4de56 Improved safety check [skip ci] 2024-04-15 14:55:32 -07:00
Andrew Kane
2c82f05503 Improved safety check [skip ci] 2024-04-15 14:51:24 -07:00
Andrew Kane
f655166639 Improved safety check [skip ci] 2024-04-15 14:50:28 -07:00
Andrew Kane
7580e99205 Improved safety check [skip ci] 2024-04-15 14:49:40 -07:00
Andrew Kane
a415420a1c Updated l2_normalize to remove zeros for sparsevec 2024-04-15 14:42:53 -07:00
Andrew Kane
cadfc72b75 Fixed compilation on Windows 2024-04-15 14:14:25 -07:00
Andrew Kane
ceeba6a134 Added todo [skip ci] 2024-04-15 14:11:17 -07:00
Andrew Kane
127ecdd650 Added l2_normalize function for sparsevec 2024-04-15 14:05:18 -07:00
Andrew Kane
10dacfd991 Updated indexes to use l2_normalize functions 2024-04-15 13:56:50 -07:00
Andrew Kane
c282627ce5 Improved cosine distance tests for halfvec [skip ci] 2024-04-15 10:51:52 -07:00
Andrew Kane
312da84536 Added CPU dispatching for vector cosine distance [skip ci] 2024-04-15 10:47:06 -07:00
Andrew Kane
2913c9f0b6 Moved vector L1 distance to separate function [skip ci] 2024-04-15 10:35:16 -07:00
Andrew Kane
fac88d0fd4 Moved vector cosine similarity to separate function [skip ci] 2024-04-15 10:33:23 -07:00
Andrew Kane
ba99255bbb Switched to float for consistency with other distance functions 2024-04-15 10:20:27 -07:00
Andrew Kane
55845bfd5f Added SIMD version of cosine distance 2024-04-15 10:01:05 -07:00
Andrew Kane
2d33e95a73 Added tests for operators [skip ci] 2024-04-15 01:04:16 -07:00
Andrew Kane
d3e5a87df6 Improved sparsevec test [skip ci] 2024-04-15 00:58:47 -07:00
Andrew Kane
f1a46f1025 Improved sparsevec tests [skip ci] 2024-04-15 00:57:53 -07:00
Andrew Kane
df56e9f95f Improved sparsevec tests [skip ci] 2024-04-15 00:55:52 -07:00
Andrew Kane
0f2b7c3afa Improved tests [skip ci] 2024-04-15 00:53:44 -07:00
Andrew Kane
dffb061e89 Improved tests [skip ci] 2024-04-15 00:46:19 -07:00
Andrew Kane
ac29024f10 Improved tests [skip ci] 2024-04-15 00:41:30 -07:00
Andrew Kane
5f739e97e0 Improved tests for l1_distance [skip ci] 2024-04-15 00:37:38 -07:00
Andrew Kane
b12cd121a5 Use fabsf for l1_distance 2024-04-14 23:33:27 -07:00
Andrew Kane
2a2c57db23 Fixed flaky tests [skip ci] 2024-04-14 23:32:54 -07:00
Andrew Kane
bdeb125a97 Simplified concat tests [skip ci] 2024-04-14 23:18:07 -07:00
Andrew Kane
38b223b4bd Added concatenate operator for vectors 2024-04-14 23:12:07 -07:00
Andrew Kane
4f6c4850d9 Added l1_distance function for sparsevec [skip ci] 2024-04-14 22:59:28 -07:00
Andrew Kane
b70fb2b3f4 Added l2_normalize function - closes #220 2024-04-14 20:53:05 -07:00
Andrew Kane
00308491d3 Added CPU dispatching for distance functions on Linux x86-64 - closes #311
Co-authored-by: Arda Aytekin <arda.aytekin@microsoft.com>
2024-04-14 19:30:41 -07:00
Andrew Kane
fe9a9a0d0f Renamed quantize_binary to binary_quantize 2024-04-14 16:57:28 -07:00
Andrew Kane
3e48a3f69d Fixed upgrade script [skip ci] 2024-04-14 16:41:20 -07:00
Andrew Kane
8751404a94 Renamed halfvec_dims to vector_dims [skip ci] 2024-04-14 16:40:16 -07:00
Andrew Kane
8118d65b48 Moved functions [skip ci] 2024-04-14 16:36:27 -07:00
Andrew Kane
3cc227da87 Moved functions [skip ci] 2024-04-14 16:33:46 -07:00
Andrew Kane
44e536b4ae Renamed functions to l2_norm [skip ci] 2024-04-14 16:29:27 -07:00
Andrew Kane
bc199a33cd Added sum for half vectors 2024-04-14 15:16:17 -07:00
Andrew Kane
e146f3cfb6 Added avg for half vectors [skip ci] 2024-04-14 15:11:11 -07:00
Andrew Kane
92d08bb6f5 Fixed regression test list for Windows [skip ci] 2024-04-14 14:14:18 -07:00
Andrew Kane
00d7c8b724 Fixed regression test list for Windows 2024-04-14 14:05:44 -07:00
Andrew Kane
b4b914a580 Fixed sparsevec_cmp_internal 2024-04-14 14:04:03 -07:00
Andrew Kane
fc3ebf4d7d Updated casts [skip ci] 2024-04-14 14:02:29 -07:00
Andrew Kane
0507fc9369 Added support for ordering halfvec and sparsevec columns 2024-04-14 13:58:54 -07:00
Andrew Kane
a5d51ed539 Updated readme [skip ci] 2024-04-14 13:43:15 -07:00
Andrew Kane
88788472ba Added comparison operators for sparsevec 2024-04-14 13:40:37 -07:00
Andrew Kane
c68c2867fd Added more functions for halfvec 2024-04-14 13:12:08 -07:00
Andrew Kane
45cea30943 Updated pkg instructions [skip ci] 2024-04-14 09:24:18 -07:00
Andrew Kane
31a007933c Updated readme [skip ci] 2024-04-14 08:40:27 -07:00
68 changed files with 3651 additions and 457 deletions

View File

@@ -115,6 +115,8 @@ jobs:
sudo -u postgres make prove_installcheck
env:
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
- if: ${{ failure() }}
run: cat pgvector/regression.diffs
valgrind:
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
runs-on: ubuntu-latest
@@ -123,6 +125,7 @@ jobs:
- uses: ankane/setup-postgres-valgrind@v1
with:
postgres-version: 16
check-ub: yes
- run: make OPTFLAGS=""
- run: sudo --preserve-env=PG_CONFIG make install
- run: make installcheck

View File

@@ -2,11 +2,14 @@
- Added `halfvec` type
- Added `sparsevec` type
- Added support for `bit` vectors to HNSW
- Added support for indexing `bit` type
- Added support for indexing L1 distance with HNSW
- Added `binary_quantize` function
- Added `hamming_distance` function
- Added `jaccard_distance` function
- Added `quantize_binary` function
- Added `l2_normalize` function
- Added `subvector` function
- Added concatenate operator for vectors
- Added CPU dispatching for distance functions on Linux x86-64
- Updated comparison operators to support vectors with different dimensions

View File

@@ -3,7 +3,7 @@ EXTVERSION = 0.6.2
MODULE_big = vector
DATA = $(wildcard sql/*--*.sql)
OBJS = src/bitvector.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
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
TESTS = $(wildcard test/sql/*.sql)

View File

@@ -1,10 +1,10 @@
EXTENSION = vector
EXTVERSION = 0.6.2
OBJS = src\bitvector.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
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 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_options ivfflat_unlogged ivfflat_vector_cosine ivfflat_vector_ip ivfflat_vector_l2 sparsevec_functions sparsevec_input vector_functions vector_input
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_OPTS = --inputdir=test --load-extension=$(EXTENSION)
# For /arch flags

View File

@@ -81,7 +81,7 @@ Get the nearest neighbors by L2 distance
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
```
Also supports inner product (`<#>`) and cosine distance (`<=>`)
Also supports inner product (`<#>`), cosine distance (`<=>`), and L1 distance (`<+>`, unreleased)
Note: `<#>` returns the negative inner product since Postgres only supports `ASC` order index scans on operators
@@ -138,6 +138,13 @@ Get the nearest neighbors to a vector
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
```
Supported distance functions are:
- `<->` - L2 distance
- `<#>` - (negative) inner product
- `<=>` - cosine distance
- `<+>` - L1 distance (unreleased)
Get the nearest neighbors to a row
```sql
@@ -221,6 +228,12 @@ Cosine distance
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
```
L1 distance - unreleased
```sql
CREATE INDEX ON items USING hnsw (embedding vector_l1_ops);
```
Hamming distance - unreleased
```sql
@@ -343,10 +356,17 @@ Cosine distance
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
```
Hamming distance - unreleased
```sql
CREATE INDEX ON items USING ivfflat (embedding bit_hamming_ops) WITH (lists = 100);
```
Supported types are:
- `vector` - up to 2,000 dimensions
- `halfvec` - up to 4,000 dimensions (unreleased)
- `bit` - up to 64,000 dimensions (unreleased)
### Query Options
@@ -476,20 +496,20 @@ Also supports Jaccard distance (`<%>`)
Use expression indexing for binary quantization
```sql
CREATE INDEX ON items USING hnsw ((quantize_binary(embedding)::bit(3)) bit_hamming_ops);
CREATE INDEX ON items USING hnsw ((binary_quantize(embedding)::bit(3)) bit_hamming_ops);
```
Get the nearest neighbors by Hamming distance
```sql
SELECT * FROM items ORDER BY quantize_binary(embedding)::bit(3) <~> quantize_binary('[1,-2,3]') LIMIT 5;
SELECT * FROM items ORDER BY binary_quantize(embedding)::bit(3) <~> binary_quantize('[1,-2,3]') LIMIT 5;
```
Re-rank by the original vectors for better recall
```sql
SELECT * FROM (
SELECT * FROM items ORDER BY quantize_binary(embedding)::bit(3) <~> quantize_binary('[1,-2,3]') LIMIT 20
SELECT * FROM items ORDER BY binary_quantize(embedding)::bit(3) <~> binary_quantize('[1,-2,3]') LIMIT 20
) ORDER BY embedding <=> '[1,-2,3]' LIMIT 5;
```
@@ -509,7 +529,7 @@ Insert vectors
INSERT INTO items (embedding) VALUES ('{1:1,3:2,5:3}/5'), ('{1:4,3:5,5:6}/5');
```
Note: The format is `{index1:value1,index2:value2,...}/dimensions` and indices start at 1 like SQL arrays
The format is `{index1:value1,index2:value2}/dimensions` and indices start at 1 like SQL arrays
Get the nearest neighbors by L2 distance
@@ -528,7 +548,7 @@ SELECT id, content FROM items, plainto_tsquery('hello search') query
You can use [Reciprocal Rank Fusion](https://github.com/pgvector/pgvector-python/blob/master/examples/hybrid_search_rrf.py) or a [cross-encoder](https://github.com/pgvector/pgvector-python/blob/master/examples/hybrid_search.py) to combine results.
## Subvector Indexing
## Indexing Subvectors
*Unreleased*
@@ -844,19 +864,22 @@ Operator | Description | Added
\+ | element-wise addition |
\- | element-wise subtraction |
\* | element-wise multiplication | 0.5.0
\|\| | concatenate | unreleased
<-> | Euclidean distance |
<#> | negative inner product |
<=> | cosine distance |
<+> | taxicab distance | unreleased
### Vector Functions
Function | Description | Added
--- | --- | ---
binary_quantize(vector) → bit | binary quantize | unreleased
cosine_distance(vector, vector) → double precision | cosine distance |
inner_product(vector, vector) → double precision | inner product |
l2_distance(vector, vector) → double precision | Euclidean distance |
l1_distance(vector, vector) → double precision | taxicab distance | 0.5.0
quantize_binary(vector) → bit | quantize | unreleased
l2_distance(vector, vector) → double precision | Euclidean distance |
l2_normalize(vector) → vector | Normalize with Euclidean norm | unreleased
subvector(vector, integer, integer) → vector | subvector | unreleased
vector_dims(vector) → integer | number of dimensions |
vector_norm(vector) → double precision | Euclidean norm |
@@ -876,20 +899,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
### 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
l2_distance(halfvec, halfvec) → double precision | Euclidean distance | unreleased
l1_distance(halfvec, halfvec) → double precision | taxicab distance | unreleased
quantize_binary(halfvec) → bit | quantize | 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
### Halfvec Aggregate Functions
Function | Description | Added
--- | --- | ---
avg(halfvec) → halfvec | average | unreleased
sum(halfvec) → halfvec | sum | unreleased
### Bit Type
@@ -920,6 +958,7 @@ Operator | Description | Added
<-> | Euclidean distance | unreleased
<#> | negative inner product | unreleased
<=> | cosine distance | unreleased
<+> | taxicab distance | unreleased
### Sparsevec Functions
@@ -927,8 +966,10 @@ Function | Description | Added
--- | --- | ---
cosine_distance(sparsevec, sparsevec) → double precision | cosine distance | unreleased
inner_product(sparsevec, sparsevec) → double precision | inner product | unreleased
l2_distance(sparsevec, sparsevec) → double precision | Euclidean distance | 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
## Installation Notes - Linux and Mac
@@ -1055,7 +1096,7 @@ Note: Replace `16` with your Postgres server version
Install the FreeBSD package with:
```sh
pkg install postgresql15-pg_vector
pkg install postgresql15-pgvector
```
or the port with:

View File

@@ -1,18 +1,44 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.0'" to load this file. \quit
CREATE FUNCTION quantize_binary(vector) RETURNS bit
CREATE FUNCTION l2_normalize(vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION binary_quantize(vector) RETURNS bit
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION subvector(vector, int, int) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_concat(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE OPERATOR <+> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = l1_distance,
COMMUTATOR = '<+>'
);
CREATE OPERATOR || (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_concat
);
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 = '<~>'
@@ -23,15 +49,24 @@ CREATE OPERATOR <%> (
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 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 1 jaccard_distance(bit, bit),
FUNCTION 4 bit_hnsw_support(internal);
CREATE TYPE halfvec;
@@ -71,15 +106,54 @@ CREATE FUNCTION cosine_distance(halfvec, halfvec) RETURNS float8
CREATE FUNCTION l1_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME', 'halfvec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_dims(halfvec) RETURNS integer
AS 'MODULE_PATHNAME', 'halfvec_vector_dims' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION quantize_binary(halfvec) RETURNS bit
AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l2_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME', 'halfvec_l2_norm' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l2_normalize(halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME', 'halfvec_l2_normalize' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION binary_quantize(halfvec) RETURNS bit
AS 'MODULE_PATHNAME', 'halfvec_binary_quantize' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION subvector(halfvec, int, int) RETURNS halfvec
AS 'MODULE_PATHNAME', 'halfvec_subvector' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_add(halfvec, halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_sub(halfvec, halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_mul(halfvec, halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_concat(halfvec, halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_lt(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_le(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_eq(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_ne(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_ge(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_gt(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_cmp(halfvec, halfvec) RETURNS int4
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_l2_squared_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -89,9 +163,43 @@ CREATE FUNCTION halfvec_negative_inner_product(halfvec, halfvec) RETURNS float8
CREATE FUNCTION halfvec_spherical_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_accum(double precision[], halfvec) RETURNS double precision[]
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
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 AGGREGATE avg(halfvec) (
SFUNC = halfvec_accum,
STYPE = double precision[],
FINALFUNC = halfvec_avg,
COMBINEFUNC = vector_combine,
INITCOND = '{0}',
PARALLEL = SAFE
);
CREATE AGGREGATE sum(halfvec) (
SFUNC = halfvec_add,
STYPE = halfvec,
COMBINEFUNC = halfvec_add,
PARALLEL = SAFE
);
CREATE FUNCTION halfvec(halfvec, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_to_vector(halfvec, integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_to_halfvec(vector, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION array_to_halfvec(integer[], integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -110,8 +218,14 @@ CREATE FUNCTION halfvec_to_float4(halfvec, integer, boolean) RETURNS real[]
CREATE CAST (halfvec AS halfvec)
WITH FUNCTION halfvec(halfvec, integer, boolean) AS IMPLICIT;
CREATE CAST (halfvec AS vector)
WITH FUNCTION halfvec_to_vector(halfvec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (vector AS halfvec)
WITH FUNCTION vector_to_halfvec(vector, integer, boolean) AS IMPLICIT;
CREATE CAST (halfvec AS real[])
WITH FUNCTION halfvec_to_float4(halfvec, integer, boolean) AS IMPLICIT;
WITH FUNCTION halfvec_to_float4(halfvec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (integer[] AS halfvec)
WITH FUNCTION array_to_halfvec(integer[], integer, boolean) AS ASSIGNMENT;
@@ -140,54 +254,125 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>'
);
CREATE OPERATOR <+> (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = l1_distance,
COMMUTATOR = '<+>'
);
CREATE OPERATOR + (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_add,
COMMUTATOR = +
);
CREATE OPERATOR - (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_sub
);
CREATE OPERATOR * (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_mul,
COMMUTATOR = *
);
CREATE OPERATOR || (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_concat
);
CREATE OPERATOR < (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_lt,
COMMUTATOR = > , NEGATOR = >= ,
RESTRICT = scalarltsel, JOIN = scalarltjoinsel
);
CREATE OPERATOR <= (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_le,
COMMUTATOR = >= , NEGATOR = > ,
RESTRICT = scalarlesel, JOIN = scalarlejoinsel
);
CREATE OPERATOR = (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_eq,
COMMUTATOR = = , NEGATOR = <> ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR <> (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_ne,
COMMUTATOR = <> , NEGATOR = = ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR >= (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_ge,
COMMUTATOR = <= , NEGATOR = < ,
RESTRICT = scalargesel, JOIN = scalargejoinsel
);
CREATE OPERATOR > (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_gt,
COMMUTATOR = < , NEGATOR = <= ,
RESTRICT = scalargtsel, JOIN = scalargtjoinsel
);
CREATE OPERATOR CLASS halfvec_ops
DEFAULT FOR TYPE halfvec USING btree AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 halfvec_cmp(halfvec, halfvec);
CREATE OPERATOR CLASS halfvec_l2_ops
FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
FUNCTION 3 l2_distance(halfvec, halfvec);
FUNCTION 3 l2_distance(halfvec, halfvec),
FUNCTION 6 halfvec_ivfflat_support(internal);
CREATE OPERATOR CLASS halfvec_ip_ops
FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops,
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
FUNCTION 4 halfvec_norm(halfvec);
FUNCTION 4 l2_norm(halfvec),
FUNCTION 5 l2_normalize(halfvec),
FUNCTION 6 halfvec_ivfflat_support(internal);
CREATE OPERATOR CLASS halfvec_cosine_ops
FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
FUNCTION 2 halfvec_norm(halfvec),
FUNCTION 2 l2_norm(halfvec),
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
FUNCTION 4 halfvec_norm(halfvec);
FUNCTION 4 l2_norm(halfvec),
FUNCTION 5 l2_normalize(halfvec),
FUNCTION 6 halfvec_ivfflat_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 1 halfvec_l2_squared_distance(halfvec, halfvec),
FUNCTION 4 halfvec_hnsw_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 1 halfvec_negative_inner_product(halfvec, halfvec),
FUNCTION 4 halfvec_hnsw_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 halfvec_norm(halfvec);
FUNCTION 2 l2_norm(halfvec),
FUNCTION 3 l2_normalize(halfvec),
FUNCTION 4 halfvec_hnsw_support(internal);
CREATE FUNCTION halfvec_to_vector(halfvec, integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_to_halfvec(vector, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (halfvec AS vector)
WITH FUNCTION halfvec_to_vector(halfvec, integer, boolean) AS IMPLICIT;
CREATE CAST (vector AS halfvec)
WITH FUNCTION vector_to_halfvec(vector, integer, boolean) AS IMPLICIT;
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);
CREATE TYPE sparsevec;
@@ -224,7 +409,34 @@ CREATE FUNCTION inner_product(sparsevec, sparsevec) RETURNS float8
CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_norm(sparsevec) RETURNS float8
CREATE FUNCTION l1_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l2_norm(sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_l2_norm' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l2_normalize(sparsevec) RETURNS sparsevec
AS 'MODULE_PATHNAME', 'sparsevec_l2_normalize' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_lt(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_le(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_eq(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_ne(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_ge(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_gt(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_cmp(sparsevec, sparsevec) RETURNS int4
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_l2_squared_distance(sparsevec, sparsevec) RETURNS float8
@@ -242,15 +454,30 @@ CREATE FUNCTION vector_to_sparsevec(vector, integer, boolean) RETURNS sparsevec
CREATE FUNCTION sparsevec_to_vector(sparsevec, integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_to_sparsevec(halfvec, integer, boolean) RETURNS sparsevec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
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;
CREATE CAST (sparsevec AS vector)
WITH FUNCTION sparsevec_to_vector(sparsevec, integer, boolean) AS IMPLICIT;
WITH FUNCTION sparsevec_to_vector(sparsevec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (vector AS sparsevec)
WITH FUNCTION vector_to_sparsevec(vector, integer, boolean) AS IMPLICIT;
CREATE CAST (sparsevec AS halfvec)
WITH FUNCTION sparsevec_to_halfvec(sparsevec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (halfvec AS sparsevec)
WITH FUNCTION halfvec_to_sparsevec(halfvec, integer, boolean) AS IMPLICIT;
CREATE OPERATOR <-> (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = l2_distance,
COMMUTATOR = '<->'
@@ -266,18 +493,78 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>'
);
CREATE OPERATOR <+> (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = l1_distance,
COMMUTATOR = '<+>'
);
CREATE OPERATOR < (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_lt,
COMMUTATOR = > , NEGATOR = >= ,
RESTRICT = scalarltsel, JOIN = scalarltjoinsel
);
CREATE OPERATOR <= (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_le,
COMMUTATOR = >= , NEGATOR = > ,
RESTRICT = scalarlesel, JOIN = scalarlejoinsel
);
CREATE OPERATOR = (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_eq,
COMMUTATOR = = , NEGATOR = <> ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR <> (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_ne,
COMMUTATOR = <> , NEGATOR = = ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR >= (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_ge,
COMMUTATOR = <= , NEGATOR = < ,
RESTRICT = scalargesel, JOIN = scalargejoinsel
);
CREATE OPERATOR > (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_gt,
COMMUTATOR = < , NEGATOR = <= ,
RESTRICT = scalargtsel, JOIN = scalargtjoinsel
);
CREATE OPERATOR CLASS sparsevec_ops
DEFAULT FOR TYPE sparsevec USING btree AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 sparsevec_cmp(sparsevec, sparsevec);
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 1 sparsevec_l2_squared_distance(sparsevec, sparsevec),
FUNCTION 4 sparsevec_hnsw_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 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
FUNCTION 4 sparsevec_hnsw_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 sparsevec_norm(sparsevec);
FUNCTION 2 l2_norm(sparsevec),
FUNCTION 3 l2_normalize(sparsevec),
FUNCTION 4 sparsevec_hnsw_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);

View File

@@ -49,6 +49,17 @@ CREATE FUNCTION vector_dims(vector) RETURNS integer
CREATE FUNCTION vector_norm(vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l2_normalize(vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION binary_quantize(vector) RETURNS bit
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION subvector(vector, int, int) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- vector private functions
CREATE FUNCTION vector_add(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -58,14 +69,9 @@ CREATE FUNCTION vector_sub(vector, vector) RETURNS vector
CREATE FUNCTION vector_mul(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION quantize_binary(vector) RETURNS bit
CREATE FUNCTION vector_concat(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION subvector(vector, int, int) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- vector private functions
CREATE FUNCTION vector_lt(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -180,6 +186,11 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>'
);
CREATE OPERATOR <+> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = l1_distance,
COMMUTATOR = '<+>'
);
CREATE OPERATOR + (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_add,
COMMUTATOR = +
@@ -194,6 +205,10 @@ CREATE OPERATOR * (
COMMUTATOR = *
);
CREATE OPERATOR || (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_concat
);
CREATE OPERATOR < (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_lt,
COMMUTATOR = > , NEGATOR = >= ,
@@ -294,6 +309,11 @@ CREATE OPERATOR CLASS vector_cosine_ops
FUNCTION 1 vector_negative_inner_product(vector, vector),
FUNCTION 2 vector_norm(vector);
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);
-- bit functions
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
@@ -302,6 +322,16 @@ CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
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 = '<~>'
@@ -312,15 +342,26 @@ CREATE OPERATOR <%> (
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 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 1 jaccard_distance(bit, bit),
FUNCTION 4 bit_hnsw_support(internal);
-- halfvec type
@@ -364,17 +405,56 @@ CREATE FUNCTION cosine_distance(halfvec, halfvec) RETURNS float8
CREATE FUNCTION l1_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME', 'halfvec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_dims(halfvec) RETURNS integer
AS 'MODULE_PATHNAME', 'halfvec_vector_dims' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION quantize_binary(halfvec) RETURNS bit
AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l2_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME', 'halfvec_l2_norm' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l2_normalize(halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME', 'halfvec_l2_normalize' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION binary_quantize(halfvec) RETURNS bit
AS 'MODULE_PATHNAME', 'halfvec_binary_quantize' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION subvector(halfvec, int, int) RETURNS halfvec
AS 'MODULE_PATHNAME', 'halfvec_subvector' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- halfvec private functions
CREATE FUNCTION halfvec_add(halfvec, halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_sub(halfvec, halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_mul(halfvec, halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_concat(halfvec, halfvec) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_lt(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_le(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_eq(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_ne(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_ge(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_gt(halfvec, halfvec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_cmp(halfvec, halfvec) RETURNS int4
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_l2_squared_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -384,11 +464,47 @@ CREATE FUNCTION halfvec_negative_inner_product(halfvec, halfvec) RETURNS float8
CREATE FUNCTION halfvec_spherical_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_accum(double precision[], halfvec) RETURNS double precision[]
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
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;
-- halfvec aggregates
CREATE AGGREGATE avg(halfvec) (
SFUNC = halfvec_accum,
STYPE = double precision[],
FINALFUNC = halfvec_avg,
COMBINEFUNC = vector_combine,
INITCOND = '{0}',
PARALLEL = SAFE
);
CREATE AGGREGATE sum(halfvec) (
SFUNC = halfvec_add,
STYPE = halfvec,
COMBINEFUNC = halfvec_add,
PARALLEL = SAFE
);
-- halfvec cast functions
CREATE FUNCTION halfvec(halfvec, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_to_vector(halfvec, integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_to_halfvec(vector, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION array_to_halfvec(integer[], integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -409,8 +525,14 @@ CREATE FUNCTION halfvec_to_float4(halfvec, integer, boolean) RETURNS real[]
CREATE CAST (halfvec AS halfvec)
WITH FUNCTION halfvec(halfvec, integer, boolean) AS IMPLICIT;
CREATE CAST (halfvec AS vector)
WITH FUNCTION halfvec_to_vector(halfvec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (vector AS halfvec)
WITH FUNCTION vector_to_halfvec(vector, integer, boolean) AS IMPLICIT;
CREATE CAST (halfvec AS real[])
WITH FUNCTION halfvec_to_float4(halfvec, integer, boolean) AS IMPLICIT;
WITH FUNCTION halfvec_to_float4(halfvec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (integer[] AS halfvec)
WITH FUNCTION array_to_halfvec(integer[], integer, boolean) AS ASSIGNMENT;
@@ -441,58 +563,127 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>'
);
CREATE OPERATOR <+> (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = l1_distance,
COMMUTATOR = '<+>'
);
CREATE OPERATOR + (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_add,
COMMUTATOR = +
);
CREATE OPERATOR - (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_sub
);
CREATE OPERATOR * (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_mul,
COMMUTATOR = *
);
CREATE OPERATOR || (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_concat
);
CREATE OPERATOR < (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_lt,
COMMUTATOR = > , NEGATOR = >= ,
RESTRICT = scalarltsel, JOIN = scalarltjoinsel
);
CREATE OPERATOR <= (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_le,
COMMUTATOR = >= , NEGATOR = > ,
RESTRICT = scalarlesel, JOIN = scalarlejoinsel
);
CREATE OPERATOR = (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_eq,
COMMUTATOR = = , NEGATOR = <> ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR <> (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_ne,
COMMUTATOR = <> , NEGATOR = = ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR >= (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_ge,
COMMUTATOR = <= , NEGATOR = < ,
RESTRICT = scalargesel, JOIN = scalargejoinsel
);
CREATE OPERATOR > (
LEFTARG = halfvec, RIGHTARG = halfvec, PROCEDURE = halfvec_gt,
COMMUTATOR = < , NEGATOR = <= ,
RESTRICT = scalargtsel, JOIN = scalargtjoinsel
);
-- halfvec opclasses
CREATE OPERATOR CLASS halfvec_ops
DEFAULT FOR TYPE halfvec USING btree AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 halfvec_cmp(halfvec, halfvec);
CREATE OPERATOR CLASS halfvec_l2_ops
FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
FUNCTION 3 l2_distance(halfvec, halfvec);
FUNCTION 3 l2_distance(halfvec, halfvec),
FUNCTION 6 halfvec_ivfflat_support(internal);
CREATE OPERATOR CLASS halfvec_ip_ops
FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops,
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
FUNCTION 4 halfvec_norm(halfvec);
FUNCTION 4 l2_norm(halfvec),
FUNCTION 5 l2_normalize(halfvec),
FUNCTION 6 halfvec_ivfflat_support(internal);
CREATE OPERATOR CLASS halfvec_cosine_ops
FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
FUNCTION 2 halfvec_norm(halfvec),
FUNCTION 2 l2_norm(halfvec),
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
FUNCTION 4 halfvec_norm(halfvec);
FUNCTION 4 l2_norm(halfvec),
FUNCTION 5 l2_normalize(halfvec),
FUNCTION 6 halfvec_ivfflat_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 1 halfvec_l2_squared_distance(halfvec, halfvec),
FUNCTION 4 halfvec_hnsw_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 1 halfvec_negative_inner_product(halfvec, halfvec),
FUNCTION 4 halfvec_hnsw_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 halfvec_norm(halfvec);
FUNCTION 2 l2_norm(halfvec),
FUNCTION 3 l2_normalize(halfvec),
FUNCTION 4 halfvec_hnsw_support(internal);
-- extension casts
CREATE FUNCTION halfvec_to_vector(halfvec, integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_to_halfvec(vector, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (halfvec AS vector)
WITH FUNCTION halfvec_to_vector(halfvec, integer, boolean) AS IMPLICIT;
CREATE CAST (vector AS halfvec)
WITH FUNCTION vector_to_halfvec(vector, integer, boolean) AS IMPLICIT;
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);
--- sparsevec type
@@ -533,11 +724,38 @@ CREATE FUNCTION inner_product(sparsevec, sparsevec) RETURNS float8
CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_norm(sparsevec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l1_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l2_norm(sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_l2_norm' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION l2_normalize(sparsevec) RETURNS sparsevec
AS 'MODULE_PATHNAME', 'sparsevec_l2_normalize' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- sparsevec private functions
CREATE FUNCTION sparsevec_lt(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_le(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_eq(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_ne(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_ge(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_gt(sparsevec, sparsevec) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_cmp(sparsevec, sparsevec) RETURNS int4
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_l2_squared_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -555,17 +773,32 @@ CREATE FUNCTION vector_to_sparsevec(vector, integer, boolean) RETURNS sparsevec
CREATE FUNCTION sparsevec_to_vector(sparsevec, integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_to_sparsevec(halfvec, integer, boolean) RETURNS sparsevec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
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)
WITH FUNCTION sparsevec(sparsevec, integer, boolean) AS IMPLICIT;
CREATE CAST (sparsevec AS vector)
WITH FUNCTION sparsevec_to_vector(sparsevec, integer, boolean) AS IMPLICIT;
WITH FUNCTION sparsevec_to_vector(sparsevec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (vector AS sparsevec)
WITH FUNCTION vector_to_sparsevec(vector, integer, boolean) AS IMPLICIT;
CREATE CAST (sparsevec AS halfvec)
WITH FUNCTION sparsevec_to_halfvec(sparsevec, integer, boolean) AS ASSIGNMENT;
CREATE CAST (halfvec AS sparsevec)
WITH FUNCTION halfvec_to_sparsevec(halfvec, integer, boolean) AS IMPLICIT;
-- sparsevec operators
CREATE OPERATOR <-> (
@@ -583,20 +816,80 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>'
);
CREATE OPERATOR <+> (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = l1_distance,
COMMUTATOR = '<+>'
);
CREATE OPERATOR < (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_lt,
COMMUTATOR = > , NEGATOR = >= ,
RESTRICT = scalarltsel, JOIN = scalarltjoinsel
);
CREATE OPERATOR <= (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_le,
COMMUTATOR = >= , NEGATOR = > ,
RESTRICT = scalarlesel, JOIN = scalarlejoinsel
);
CREATE OPERATOR = (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_eq,
COMMUTATOR = = , NEGATOR = <> ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR <> (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_ne,
COMMUTATOR = <> , NEGATOR = = ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR >= (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_ge,
COMMUTATOR = <= , NEGATOR = < ,
RESTRICT = scalargesel, JOIN = scalargejoinsel
);
CREATE OPERATOR > (
LEFTARG = sparsevec, RIGHTARG = sparsevec, PROCEDURE = sparsevec_gt,
COMMUTATOR = < , NEGATOR = <= ,
RESTRICT = scalargtsel, JOIN = scalargtjoinsel
);
-- sparsevec opclasses
CREATE OPERATOR CLASS sparsevec_ops
DEFAULT FOR TYPE sparsevec USING btree AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 sparsevec_cmp(sparsevec, sparsevec);
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 1 sparsevec_l2_squared_distance(sparsevec, sparsevec),
FUNCTION 4 sparsevec_hnsw_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 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
FUNCTION 4 sparsevec_hnsw_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 sparsevec_norm(sparsevec);
FUNCTION 2 l2_norm(sparsevec),
FUNCTION 3 l2_normalize(sparsevec),
FUNCTION 4 sparsevec_hnsw_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);

217
src/bitutils.c Normal file
View File

@@ -0,0 +1,217 @@
#include "postgres.h"
#include "bitutils.h"
#include "port/pg_bitutils.h"
#ifdef BIT_DISPATCH
#include <immintrin.h>
#if defined(HAVE__GET_CPUID)
#include <cpuid.h>
#else
#include <intrin.h>
#endif
#ifdef _MSC_VER
#define TARGET_AVX512_POPCOUNT
#else
#define TARGET_AVX512_POPCOUNT __attribute__((target("avx512f,avx512vpopcntdq")))
#endif
#endif
/* Disable for LLVM due to crash with bitcode generation */
#if defined(USE_TARGET_CLONES) && !defined(__POPCNT__) && !defined(__llvm__)
#define BIT_TARGET_CLONES __attribute__((target_clones("default", "popcnt")))
#else
#define BIT_TARGET_CLONES
#endif
/* Use built-ins when possible for inlining */
#if defined(HAVE__BUILTIN_POPCOUNT) && defined(HAVE_LONG_INT_64)
#define popcount64(x) __builtin_popcountl(x)
#elif defined(HAVE__BUILTIN_POPCOUNT) && defined(HAVE_LONG_LONG_INT_64)
#define popcount64(x) __builtin_popcountll(x)
#elif !defined(_MSC_VER)
/* Fails to resolve with MSVC */
#define popcount64(x) pg_popcount64(x)
#endif
uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance);
double (*BitJaccardDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 ab, uint64 aa, uint64 bb);
BIT_TARGET_CLONES static uint64
BitHammingDistanceDefault(uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance)
{
#ifdef popcount64
for (; bytes >= sizeof(uint64); bytes -= sizeof(uint64))
{
uint64 axs;
uint64 bxs;
/* Ensure aligned */
memcpy(&axs, ax, sizeof(uint64));
memcpy(&bxs, bx, sizeof(uint64));
distance += popcount64(axs ^ bxs);
ax += sizeof(uint64);
bx += sizeof(uint64);
}
#endif
for (uint32 i = 0; i < bytes; i++)
distance += pg_number_of_ones[ax[i] ^ bx[i]];
return distance;
}
#ifdef BIT_DISPATCH
TARGET_AVX512_POPCOUNT static uint64
BitHammingDistanceAvx512Popcount(uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance)
{
__m512i dist = _mm512_setzero_si512();
for (; bytes >= sizeof(__m512i); bytes -= sizeof(__m512i))
{
__m512i axs = _mm512_loadu_si512((const __m512i *) ax);
__m512i bxs = _mm512_loadu_si512((const __m512i *) bx);
dist = _mm512_add_epi64(dist, _mm512_popcnt_epi64(_mm512_xor_si512(axs, bxs)));
ax += sizeof(__m512i);
bx += sizeof(__m512i);
}
distance += _mm512_reduce_add_epi64(dist);
return BitHammingDistanceDefault(bytes, ax, bx, distance);
}
#endif
BIT_TARGET_CLONES static double
BitJaccardDistanceDefault(uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 ab, uint64 aa, uint64 bb)
{
#ifdef popcount64
for (; bytes >= sizeof(uint64); bytes -= sizeof(uint64))
{
uint64 axs;
uint64 bxs;
/* Ensure aligned */
memcpy(&axs, ax, sizeof(uint64));
memcpy(&bxs, bx, sizeof(uint64));
ab += popcount64(axs & bxs);
aa += popcount64(axs);
bb += popcount64(bxs);
ax += sizeof(uint64);
bx += sizeof(uint64);
}
#endif
for (uint32 i = 0; i < bytes; i++)
{
ab += pg_number_of_ones[ax[i] & bx[i]];
aa += pg_number_of_ones[ax[i]];
bb += pg_number_of_ones[bx[i]];
}
if (ab == 0)
return 1;
else
return 1 - (ab / ((double) (aa + bb - ab)));
}
#ifdef BIT_DISPATCH
TARGET_AVX512_POPCOUNT static double
BitJaccardDistanceAvx512Popcount(uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 ab, uint64 aa, uint64 bb)
{
__m512i abx = _mm512_setzero_si512();
__m512i aax = _mm512_setzero_si512();
__m512i bbx = _mm512_setzero_si512();
for (; bytes >= sizeof(__m512i); bytes -= sizeof(__m512i))
{
__m512i axs = _mm512_loadu_si512((const __m512i *) ax);
__m512i bxs = _mm512_loadu_si512((const __m512i *) bx);
abx = _mm512_add_epi64(abx, _mm512_popcnt_epi64(_mm512_and_si512(axs, bxs)));
aax = _mm512_add_epi64(aax, _mm512_popcnt_epi64(axs));
bbx = _mm512_add_epi64(bbx, _mm512_popcnt_epi64(bxs));
ax += sizeof(__m512i);
bx += sizeof(__m512i);
}
ab += _mm512_reduce_add_epi64(abx);
aa += _mm512_reduce_add_epi64(aax);
bb += _mm512_reduce_add_epi64(bbx);
return BitJaccardDistanceDefault(bytes, ax, bx, ab, aa, bb);
}
#endif
#ifdef BIT_DISPATCH
#define CPU_FEATURE_OSXSAVE (1 << 27) /* F1 ECX */
#define CPU_FEATURE_AVX512F (1 << 16) /* F7,0 EBX */
#define CPU_FEATURE_AVX512VPOPCNTDQ (1 << 14) /* F7,0 ECX */
#ifdef _MSC_VER
#define TARGET_XSAVE
#else
#define TARGET_XSAVE __attribute__((target("xsave")))
#endif
TARGET_XSAVE static bool
SupportsAvx512Popcount()
{
unsigned int exx[4] = {0, 0, 0, 0};
#if defined(HAVE__GET_CPUID)
__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
#else
__cpuid(exx, 1);
#endif
/* Check OS supports XSAVE */
if ((exx[2] & CPU_FEATURE_OSXSAVE) != CPU_FEATURE_OSXSAVE)
return false;
/* Check XMM, YMM, and ZMM registers are enabled */
if ((_xgetbv(0) & 0xe6) != 0xe6)
return false;
#if defined(HAVE__GET_CPUID)
__get_cpuid_count(7, 0, &exx[0], &exx[1], &exx[2], &exx[3]);
#else
__cpuidex(exx, 7, 0);
#endif
/* Check AVX512F */
if ((exx[1] & CPU_FEATURE_AVX512F) != CPU_FEATURE_AVX512F)
return false;
/* Check AVX512VPOPCNTDQ */
return (exx[2] & CPU_FEATURE_AVX512VPOPCNTDQ) == CPU_FEATURE_AVX512VPOPCNTDQ;
}
#endif
void
BitvecInit(void)
{
/*
* Could skip pointer when single function, but no difference in
* performance
*/
BitHammingDistance = BitHammingDistanceDefault;
BitJaccardDistance = BitJaccardDistanceDefault;
#ifdef BIT_DISPATCH
if (SupportsAvx512Popcount())
{
BitHammingDistance = BitHammingDistanceAvx512Popcount;
BitJaccardDistance = BitJaccardDistanceAvx512Popcount;
}
#endif
}

31
src/bitutils.h Normal file
View File

@@ -0,0 +1,31 @@
#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
/* target_clones requires glibc */
#if defined(USE_DISPATCH) && defined(__gnu_linux__)
#define USE_TARGET_CLONES
#endif
#if defined(USE_DISPATCH)
#define BIT_DISPATCH
#endif
extern uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance);
extern double (*BitJaccardDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 ab, uint64 aa, uint64 bb);
void BitvecInit(void);
#endif

View File

@@ -1,7 +1,7 @@
#include "postgres.h"
#include "bitvector.h"
#include "port/pg_bitutils.h"
#include "bitutils.h"
#include "bitvec.h"
#include "utils/varbit.h"
#if PG_VERSION_NUM >= 160000
@@ -46,17 +46,10 @@ hamming_distance(PG_FUNCTION_ARGS)
{
VarBit *a = PG_GETARG_VARBIT_P(0);
VarBit *b = PG_GETARG_VARBIT_P(1);
unsigned char *ax = VARBITS(a);
unsigned char *bx = VARBITS(b);
uint64 distance = 0;
CheckDims(a, b);
/* TODO Improve performance */
for (uint32 i = 0; i < VARBITBYTES(a); i++)
distance += pg_number_of_ones[ax[i] ^ bx[i]];
PG_RETURN_FLOAT8((double) distance);
PG_RETURN_FLOAT8((double) BitHammingDistance(VARBITBYTES(a), VARBITS(a), VARBITS(b), 0));
}
/*
@@ -68,23 +61,8 @@ jaccard_distance(PG_FUNCTION_ARGS)
{
VarBit *a = PG_GETARG_VARBIT_P(0);
VarBit *b = PG_GETARG_VARBIT_P(1);
unsigned char *ax = VARBITS(a);
unsigned char *bx = VARBITS(b);
uint64 ab = 0;
uint64 aa;
uint64 bb;
CheckDims(a, b);
/* TODO Improve performance */
for (uint32 i = 0; i < VARBITBYTES(a); i++)
ab += pg_number_of_ones[ax[i] & bx[i]];
if (ab == 0)
PG_RETURN_FLOAT8(1);
aa = pg_popcount((char *) ax, VARBITBYTES(a));
bb = pg_popcount((char *) bx, VARBITBYTES(b));
PG_RETURN_FLOAT8(1 - (ab / ((double) (aa + bb - ab))));
PG_RETURN_FLOAT8(BitJaccardDistance(VARBITBYTES(a), VARBITS(a), VARBITS(b), 0, 0, 0));
}

View File

@@ -8,19 +8,21 @@
#if defined(HAVE__GET_CPUID)
#include <cpuid.h>
#elif defined(HAVE__CPUID)
#else
#include <intrin.h>
#endif
#ifdef _MSC_VER
#define TARGET_F16C_FMA
#define TARGET_F16C
#else
#define TARGET_F16C_FMA __attribute__((target("f16c,fma")))
#define TARGET_F16C __attribute__((target("avx,f16c,fma")))
#endif
#endif
float (*HalfvecL2SquaredDistance) (int dim, half * ax, half * bx);
float (*HalfvecInnerProduct) (int dim, half * ax, half * bx);
double (*HalfvecCosineSimilarity) (int dim, half * ax, half * bx);
float (*HalfvecL1Distance) (int dim, half * ax, half * bx);
static float
HalfvecL2SquaredDistanceDefault(int dim, half * ax, half * bx)
@@ -39,8 +41,8 @@ HalfvecL2SquaredDistanceDefault(int dim, half * ax, half * bx)
}
#ifdef HALFVEC_DISPATCH
TARGET_F16C_FMA static float
HalfvecL2SquaredDistanceF16cFma(int dim, half * ax, half * bx)
TARGET_F16C static float
HalfvecL2SquaredDistanceF16c(int dim, half * ax, half * bx)
{
float distance;
int i;
@@ -87,8 +89,8 @@ HalfvecInnerProductDefault(int dim, half * ax, half * bx)
}
#ifdef HALFVEC_DISPATCH
TARGET_F16C_FMA static float
HalfvecInnerProductF16cFma(int dim, half * ax, half * bx)
TARGET_F16C static float
HalfvecInnerProductF16c(int dim, half * ax, half * bx)
{
float distance;
int i;
@@ -117,21 +119,156 @@ HalfvecInnerProductF16cFma(int dim, half * ax, half * bx)
}
#endif
#ifdef HALFVEC_DISPATCH
#define CPU_FEATURE_FMA (1 << 12)
#define CPU_FEATURE_F16C (1 << 29)
static double
HalfvecCosineSimilarityDefault(int dim, half * ax, half * bx)
{
float similarity = 0.0;
float norma = 0.0;
float normb = 0.0;
static bool
/* Auto-vectorized */
for (int i = 0; i < dim; i++)
{
float axi = HalfToFloat4(ax[i]);
float bxi = HalfToFloat4(bx[i]);
similarity += axi * bxi;
norma += axi * axi;
normb += bxi * bxi;
}
/* Use sqrt(a * b) over sqrt(a) * sqrt(b) */
return (double) similarity / sqrt((double) norma * (double) normb);
}
#ifdef HALFVEC_DISPATCH
TARGET_F16C static double
HalfvecCosineSimilarityF16c(int dim, half * ax, half * bx)
{
float similarity;
float norma;
float normb;
int i;
float s[8];
int count = (dim / 8) * 8;
__m256 sim = _mm256_setzero_ps();
__m256 na = _mm256_setzero_ps();
__m256 nb = _mm256_setzero_ps();
for (i = 0; i < count; i += 8)
{
__m128i axi = _mm_loadu_si128((__m128i *) (ax + i));
__m128i bxi = _mm_loadu_si128((__m128i *) (bx + i));
__m256 axs = _mm256_cvtph_ps(axi);
__m256 bxs = _mm256_cvtph_ps(bxi);
sim = _mm256_fmadd_ps(axs, bxs, sim);
na = _mm256_fmadd_ps(axs, axs, na);
nb = _mm256_fmadd_ps(bxs, bxs, nb);
}
_mm256_storeu_ps(s, sim);
similarity = s[0] + s[1] + s[2] + s[3] + s[4] + s[5] + s[6] + s[7];
_mm256_storeu_ps(s, na);
norma = s[0] + s[1] + s[2] + s[3] + s[4] + s[5] + s[6] + s[7];
_mm256_storeu_ps(s, nb);
normb = s[0] + s[1] + s[2] + s[3] + s[4] + s[5] + s[6] + s[7];
/* Auto-vectorized */
for (; i < dim; i++)
{
float axi = HalfToFloat4(ax[i]);
float bxi = HalfToFloat4(bx[i]);
similarity += axi * bxi;
norma += axi * axi;
normb += bxi * bxi;
}
/* Use sqrt(a * b) over sqrt(a) * sqrt(b) */
return (double) similarity / sqrt((double) norma * (double) normb);
}
#endif
static float
HalfvecL1DistanceDefault(int dim, half * ax, half * bx)
{
float distance = 0.0;
/* Auto-vectorized */
for (int i = 0; i < dim; i++)
distance += fabsf(HalfToFloat4(ax[i]) - HalfToFloat4(bx[i]));
return distance;
}
#ifdef HALFVEC_DISPATCH
/* Does not require FMA, but keep logic simple */
TARGET_F16C static float
HalfvecL1DistanceF16c(int dim, half * ax, half * bx)
{
float distance;
int i;
float s[8];
int count = (dim / 8) * 8;
__m256 dist = _mm256_setzero_ps();
__m256 sign = _mm256_set1_ps(-0.0);
for (i = 0; i < count; i += 8)
{
__m128i axi = _mm_loadu_si128((__m128i *) (ax + i));
__m128i bxi = _mm_loadu_si128((__m128i *) (bx + i));
__m256 axs = _mm256_cvtph_ps(axi);
__m256 bxs = _mm256_cvtph_ps(bxi);
dist = _mm256_add_ps(dist, _mm256_andnot_ps(sign, _mm256_sub_ps(axs, bxs)));
}
_mm256_storeu_ps(s, dist);
distance = s[0] + s[1] + s[2] + s[3] + s[4] + s[5] + s[6] + s[7];
for (; i < dim; i++)
distance += fabsf(HalfToFloat4(ax[i]) - HalfToFloat4(bx[i]));
return distance;
}
#endif
#ifdef HALFVEC_DISPATCH
#define CPU_FEATURE_FMA (1 << 12)
#define CPU_FEATURE_OSXSAVE (1 << 27)
#define CPU_FEATURE_AVX (1 << 28)
#define CPU_FEATURE_F16C (1 << 29)
#ifdef _MSC_VER
#define TARGET_XSAVE
#else
#define TARGET_XSAVE __attribute__((target("xsave")))
#endif
TARGET_XSAVE static bool
SupportsCpuFeature(unsigned int feature)
{
unsigned int exx[4] = {0, 0, 0, 0};
#if defined(HAVE__GET_CPUID)
__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
#elif defined(HAVE__CPUID)
#else
__cpuid(exx, 1);
#endif
/* Check OS supports XSAVE */
if ((exx[2] & CPU_FEATURE_OSXSAVE) != CPU_FEATURE_OSXSAVE)
return false;
/* Check XMM and YMM registers are enabled */
if ((_xgetbv(0) & 6) != 6)
return false;
/* Now check features */
return (exx[2] & feature) == feature;
}
#endif
@@ -145,12 +282,17 @@ HalfvecInit(void)
*/
HalfvecL2SquaredDistance = HalfvecL2SquaredDistanceDefault;
HalfvecInnerProduct = HalfvecInnerProductDefault;
HalfvecCosineSimilarity = HalfvecCosineSimilarityDefault;
HalfvecL1Distance = HalfvecL1DistanceDefault;
#ifdef HALFVEC_DISPATCH
if (SupportsCpuFeature(CPU_FEATURE_FMA | CPU_FEATURE_F16C))
if (SupportsCpuFeature(CPU_FEATURE_AVX | CPU_FEATURE_F16C | CPU_FEATURE_FMA))
{
HalfvecL2SquaredDistance = HalfvecL2SquaredDistanceF16cFma;
HalfvecInnerProduct = HalfvecInnerProductF16cFma;
HalfvecL2SquaredDistance = HalfvecL2SquaredDistanceF16c;
HalfvecInnerProduct = HalfvecInnerProductF16c;
HalfvecCosineSimilarity = HalfvecCosineSimilarityF16c;
/* Does not require FMA, but keep logic simple */
HalfvecL1Distance = HalfvecL1DistanceF16c;
}
#endif
}

View File

@@ -12,6 +12,8 @@
extern float (*HalfvecL2SquaredDistance) (int dim, half * ax, half * bx);
extern float (*HalfvecInnerProduct) (int dim, half * ax, half * bx);
extern double (*HalfvecCosineSimilarity) (int dim, half * ax, half * bx);
extern float (*HalfvecL1Distance) (int dim, half * ax, half * bx);
void HalfvecInit(void);
@@ -41,6 +43,19 @@ HalfIsInf(half num)
#endif
}
/*
* Check if half is zero
*/
static inline bool
HalfIsZero(half num)
{
#ifdef FLT16_SUPPORT
return num == 0;
#else
return (num & 0x7FFF) == 0x0000;
#endif
}
/*
* Convert a half to a float4
*/

View File

@@ -2,7 +2,7 @@
#include <math.h>
#include "bitvector.h"
#include "bitvec.h"
#include "catalog/pg_type.h"
#include "common/shortest_dec.h"
#include "fmgr.h"
@@ -11,6 +11,7 @@
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "port.h" /* for strtof() */
#include "sparsevec.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/float.h"
@@ -23,6 +24,9 @@
#define TYPALIGN_INT 'i'
#endif
#define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1)
#define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1))
/*
* Get a half from a message buffer
*/
@@ -146,6 +150,38 @@ halfvec_isspace(char ch)
return false;
}
/*
* Check state array
*/
static float8 *
CheckStateArray(ArrayType *statearray, const char *caller)
{
if (ARR_NDIM(statearray) != 1 ||
ARR_DIMS(statearray)[0] < 1 ||
ARR_HASNULL(statearray) ||
ARR_ELEMTYPE(statearray) != FLOAT8OID)
elog(ERROR, "%s: expected state array", caller);
return (float8 *) ARR_DATA_PTR(statearray);
}
#if PG_VERSION_NUM < 120003
static pg_noinline void
float_overflow_error(void)
{
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value out of range: overflow")));
}
static pg_noinline void
float_underflow_error(void)
{
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value out of range: underflow")));
}
#endif
/*
* Convert textual representation to internal representation
*/
@@ -591,28 +627,11 @@ halfvec_cosine_distance(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
half *ax = a->x;
half *bx = b->x;
float distance = 0.0;
float norma = 0.0;
float normb = 0.0;
double similarity;
CheckDims(a, b);
/* Auto-vectorized */
for (int i = 0; i < a->dim; i++)
{
float axi = HalfToFloat4(ax[i]);
float bxi = HalfToFloat4(bx[i]);
distance += axi * bxi;
norma += axi * axi;
normb += bxi * bxi;
}
/* Use sqrt(a * b) over sqrt(a) * sqrt(b) */
similarity = (double) distance / sqrt((double) norma * (double) normb);
similarity = HalfvecCosineSimilarity(a->dim, a->x, b->x);
#ifdef _MSC_VER
/* /fp:fast may not propagate NaN */
@@ -664,25 +683,30 @@ halfvec_l1_distance(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
half *ax = a->x;
half *bx = b->x;
float distance = 0.0;
CheckDims(a, b);
/* Auto-vectorized */
for (int i = 0; i < a->dim; i++)
distance += fabsf(HalfToFloat4(ax[i]) - HalfToFloat4(bx[i]));
PG_RETURN_FLOAT8((double) HalfvecL1Distance(a->dim, a->x, b->x));
}
PG_RETURN_FLOAT8((double) distance);
/*
* Get the dimensions of a half vector
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_vector_dims);
Datum
halfvec_vector_dims(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
PG_RETURN_INT32(a->dim);
}
/*
* Get the L2 norm of a half vector
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_norm);
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_norm);
Datum
halfvec_norm(PG_FUNCTION_ARGS)
halfvec_l2_norm(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
half *ax = a->x;
@@ -699,12 +723,195 @@ halfvec_norm(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(norm));
}
/*
* Normalize a half vector with the L2 norm
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_normalize);
Datum
halfvec_l2_normalize(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
half *ax = a->x;
double norm = 0;
HalfVector *result;
half *rx;
result = InitHalfVector(a->dim);
rx = result->x;
/* Auto-vectorized */
for (int i = 0; i < a->dim; i++)
norm += (double) HalfToFloat4(ax[i]) * (double) HalfToFloat4(ax[i]);
norm = sqrt(norm);
/* Return zero vector for zero norm */
if (norm > 0)
{
for (int i = 0; i < a->dim; i++)
rx[i] = Float4ToHalfUnchecked(HalfToFloat4(ax[i]) / norm);
/* Check for overflow */
for (int i = 0; i < a->dim; i++)
{
if (HalfIsInf(rx[i]))
float_overflow_error();
}
}
PG_RETURN_POINTER(result);
}
/*
* Add half vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_add);
Datum
halfvec_add(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
half *ax = a->x;
half *bx = b->x;
HalfVector *result;
half *rx;
CheckDims(a, b);
result = InitHalfVector(a->dim);
rx = result->x;
/* Auto-vectorized */
for (int i = 0, imax = a->dim; i < imax; i++)
{
#ifdef FLT16_SUPPORT
rx[i] = ax[i] + bx[i];
#else
rx[i] = Float4ToHalfUnchecked(HalfToFloat4(ax[i]) + HalfToFloat4(bx[i]));
#endif
}
/* Check for overflow */
for (int i = 0, imax = a->dim; i < imax; i++)
{
if (HalfIsInf(rx[i]))
float_overflow_error();
}
PG_RETURN_POINTER(result);
}
/*
* Subtract half vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_sub);
Datum
halfvec_sub(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
half *ax = a->x;
half *bx = b->x;
HalfVector *result;
half *rx;
CheckDims(a, b);
result = InitHalfVector(a->dim);
rx = result->x;
/* Auto-vectorized */
for (int i = 0, imax = a->dim; i < imax; i++)
{
#ifdef FLT16_SUPPORT
rx[i] = ax[i] - bx[i];
#else
rx[i] = Float4ToHalfUnchecked(HalfToFloat4(ax[i]) - HalfToFloat4(bx[i]));
#endif
}
/* Check for overflow */
for (int i = 0, imax = a->dim; i < imax; i++)
{
if (HalfIsInf(rx[i]))
float_overflow_error();
}
PG_RETURN_POINTER(result);
}
/*
* Multiply half vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_mul);
Datum
halfvec_mul(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
half *ax = a->x;
half *bx = b->x;
HalfVector *result;
half *rx;
CheckDims(a, b);
result = InitHalfVector(a->dim);
rx = result->x;
/* Auto-vectorized */
for (int i = 0, imax = a->dim; i < imax; i++)
{
#ifdef FLT16_SUPPORT
rx[i] = ax[i] * bx[i];
#else
rx[i] = Float4ToHalfUnchecked(HalfToFloat4(ax[i]) * HalfToFloat4(bx[i]));
#endif
}
/* Check for overflow and underflow */
for (int i = 0, imax = a->dim; i < imax; i++)
{
if (HalfIsInf(rx[i]))
float_overflow_error();
if (HalfIsZero(rx[i]) && !(HalfIsZero(ax[i]) || HalfIsZero(bx[i])))
float_underflow_error();
}
PG_RETURN_POINTER(result);
}
/*
* Concatenate half vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_concat);
Datum
halfvec_concat(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
HalfVector *result;
int dim = a->dim + b->dim;
CheckDim(dim);
result = InitHalfVector(dim);
for (int i = 0; i < a->dim; i++)
result->x[i] = a->x[i];
for (int i = 0; i < b->dim; i++)
result->x[i + a->dim] = b->x[i];
PG_RETURN_POINTER(result);
}
/*
* Quantize a half vector
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_quantize_binary);
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_binary_quantize);
Datum
halfvec_quantize_binary(PG_FUNCTION_ARGS)
halfvec_binary_quantize(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
half *ax = a->x;
@@ -775,3 +982,212 @@ halfvec_cmp_internal(HalfVector * a, HalfVector * b)
return 0;
}
/*
* Less than
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_lt);
Datum
halfvec_lt(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
PG_RETURN_BOOL(halfvec_cmp_internal(a, b) < 0);
}
/*
* Less than or equal
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_le);
Datum
halfvec_le(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
PG_RETURN_BOOL(halfvec_cmp_internal(a, b) <= 0);
}
/*
* Equal
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_eq);
Datum
halfvec_eq(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
PG_RETURN_BOOL(halfvec_cmp_internal(a, b) == 0);
}
/*
* Not equal
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ne);
Datum
halfvec_ne(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
PG_RETURN_BOOL(halfvec_cmp_internal(a, b) != 0);
}
/*
* Greater than or equal
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ge);
Datum
halfvec_ge(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
PG_RETURN_BOOL(halfvec_cmp_internal(a, b) >= 0);
}
/*
* Greater than
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_gt);
Datum
halfvec_gt(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
PG_RETURN_BOOL(halfvec_cmp_internal(a, b) > 0);
}
/*
* Compare half vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_cmp);
Datum
halfvec_cmp(PG_FUNCTION_ARGS)
{
HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1);
PG_RETURN_INT32(halfvec_cmp_internal(a, b));
}
/*
* Accumulate half vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_accum);
Datum
halfvec_accum(PG_FUNCTION_ARGS)
{
ArrayType *statearray = PG_GETARG_ARRAYTYPE_P(0);
HalfVector *newval = PG_GETARG_HALFVEC_P(1);
float8 *statevalues;
int16 dim;
bool newarr;
float8 n;
Datum *statedatums;
half *x = newval->x;
ArrayType *result;
/* Check array before using */
statevalues = CheckStateArray(statearray, "halfvec_accum");
dim = STATE_DIMS(statearray);
newarr = dim == 0;
if (newarr)
dim = newval->dim;
else
CheckExpectedDim(dim, newval->dim);
n = statevalues[0] + 1.0;
statedatums = CreateStateDatums(dim);
statedatums[0] = Float8GetDatum(n);
if (newarr)
{
for (int i = 0; i < dim; i++)
statedatums[i + 1] = Float8GetDatum((double) HalfToFloat4(x[i]));
}
else
{
for (int i = 0; i < dim; i++)
{
double v = statevalues[i + 1] + (double) HalfToFloat4(x[i]);
/* Check for overflow */
if (isinf(v))
float_overflow_error();
statedatums[i + 1] = Float8GetDatum(v);
}
}
/* Use float8 array like float4_accum */
result = construct_array(statedatums, dim + 1,
FLOAT8OID,
sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);
pfree(statedatums);
PG_RETURN_ARRAYTYPE_P(result);
}
/*
* Average half vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_avg);
Datum
halfvec_avg(PG_FUNCTION_ARGS)
{
ArrayType *statearray = PG_GETARG_ARRAYTYPE_P(0);
float8 *statevalues;
float8 n;
uint16 dim;
HalfVector *result;
/* Check array before using */
statevalues = CheckStateArray(statearray, "halfvec_avg");
n = statevalues[0];
/* SQL defines AVG of no values to be NULL */
if (n == 0.0)
PG_RETURN_NULL();
/* Create half vector */
dim = STATE_DIMS(statearray);
CheckDim(dim);
result = InitHalfVector(dim);
for (int i = 0; i < dim; i++)
{
result->x[i] = Float4ToHalf(statevalues[i + 1] / n);
CheckElement(result->x[i]);
}
PG_RETURN_POINTER(result);
}
/*
* Convert sparse vector to half vector
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_to_halfvec);
Datum
sparsevec_to_halfvec(PG_FUNCTION_ARGS)
{
SparseVector *svec = PG_GETARG_SPARSEVEC_P(0);
int32 typmod = PG_GETARG_INT32(1);
HalfVector *result;
int dim = svec->dim;
float *values = SPARSEVEC_VALUES(svec);
CheckDim(dim);
CheckExpectedDim(typmod, dim);
result = InitHalfVector(dim);
for (int i = 0; i < svec->nnz; i++)
result->x[svec->indices[i] - 1] = Float4ToHalf(values[i]);
PG_RETURN_POINTER(result);
}

View File

@@ -5,9 +5,11 @@
#include <float.h>
#include "bitutils.h"
#include "fmgr.h"
#include "vector.h"
#if defined(__x86_64__) || defined(_M_AMD64)
#if defined(USE_DISPATCH)
#define HALFVEC_DISPATCH
#endif

View File

@@ -194,7 +194,7 @@ hnswhandler(PG_FUNCTION_ARGS)
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
amroutine->amstrategies = 0;
amroutine->amsupport = 2;
amroutine->amsupport = 4;
#if PG_VERSION_NUM >= 130000
amroutine->amoptsprocnum = 0;
#endif

View File

@@ -22,6 +22,8 @@
/* 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_VERSION 1
#define HNSW_MAGIC_NUMBER 0xA953A953
@@ -61,7 +63,8 @@ typedef enum HnswType
HNSW_TYPE_VECTOR,
HNSW_TYPE_HALFVEC,
HNSW_TYPE_BIT,
HNSW_TYPE_SPARSEVEC
HNSW_TYPE_SPARSEVEC,
HNSW_TYPE_UNSUPPORTED
} HnswType;
/* Build phases */
@@ -265,6 +268,7 @@ typedef struct HnswBuildState
/* Support functions */
FmgrInfo *procinfo;
FmgrInfo *normprocinfo;
FmgrInfo *normalizeprocinfo;
Oid collation;
/* Variables */
@@ -341,6 +345,7 @@ typedef struct HnswScanOpaqueData
/* Support functions */
FmgrInfo *procinfo;
FmgrInfo *normprocinfo;
FmgrInfo *normalizeprocinfo;
Oid collation;
} HnswScanOpaqueData;
@@ -377,7 +382,8 @@ int HnswGetM(Relation index);
int HnswGetEfConstruction(Relation index);
FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum);
HnswType HnswGetType(Relation index);
bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, HnswType type);
Datum HnswNormValue(FmgrInfo *procinfo, 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);

View File

@@ -493,8 +493,10 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
/* Normalize if needed */
if (buildstate->normprocinfo != NULL)
{
if (!HnswNormValue(buildstate->normprocinfo, buildstate->collation, &value, buildstate->type))
if (!HnswCheckNorm(buildstate->normprocinfo, buildstate->collation, value))
return false;
value = HnswNormValue(buildstate->normalizeprocinfo, buildstate->collation, value);
}
/* Get datum size */
@@ -723,6 +725,7 @@ 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);

View File

@@ -614,20 +614,21 @@ HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_ti
Datum value;
FmgrInfo *normprocinfo;
Oid collation = index->rd_indcollation[0];
HnswType type = HnswGetType(index);
/* Detoast once for all calls */
value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
/* Check value */
HnswCheckValue(value, type);
HnswCheckValue(value, HnswGetType(index));
/* Normalize if needed */
normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
if (normprocinfo != NULL)
{
if (!HnswNormValue(normprocinfo, collation, &value, type))
if (!HnswCheckNorm(normprocinfo, collation, value))
return;
value = HnswNormValue(HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC), collation, value);
}
HnswInsertTupleOnDisk(index, value, values, isnull, heap_tid, false);

View File

@@ -61,7 +61,7 @@ GetScanValue(IndexScanDesc scan)
/* Fine if normalization fails */
if (so->normprocinfo != NULL)
HnswNormValue(so->normprocinfo, so->collation, &value, HnswGetType(scan->indexRelation));
value = HnswNormValue(so->normalizeprocinfo, so->collation, value);
}
return value;
@@ -87,6 +87,7 @@ 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;

View File

@@ -5,8 +5,7 @@
#include "access/generic_xlog.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_d.h"
#include "halfutils.h"
#include "halfvec.h"
#include "fmgr.h"
#include "hnsw.h"
#include "lib/pairingheap.h"
#include "sparsevec.h"
@@ -14,8 +13,6 @@
#include "utils/datum.h"
#include "utils/memdebug.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "vector.h"
#if PG_VERSION_NUM >= 130000
#include "common/hashfn.h"
@@ -161,94 +158,40 @@ HnswOptionalProcInfo(Relation index, uint16 procnum)
HnswType
HnswGetType(Relation index)
{
FmgrInfo *procinfo = HnswOptionalProcInfo(index, HNSW_TYPE_SUPPORT_PROC);
Oid typid = TupleDescAttr(index->rd_att, 0)->atttypid;
HeapTuple tuple;
Form_pg_type type;
HnswType result;
if (typid == BITOID)
return HNSW_TYPE_BIT;
if (procinfo == NULL)
return HNSW_TYPE_VECTOR;
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", typid);
result = (HnswType) DatumGetInt32(FunctionCall1(procinfo, ObjectIdGetDatum(typid)));
type = (Form_pg_type) GETSTRUCT(tuple);
if (strcmp(NameStr(type->typname), "vector") == 0)
result = HNSW_TYPE_VECTOR;
else if (strcmp(NameStr(type->typname), "halfvec") == 0)
result = HNSW_TYPE_HALFVEC;
else if (strcmp(NameStr(type->typname), "sparsevec") == 0)
result = HNSW_TYPE_SPARSEVEC;
else
{
ReleaseSysCache(tuple);
if (result == HNSW_TYPE_UNSUPPORTED)
elog(ERROR, "type not supported for hnsw index");
}
ReleaseSysCache(tuple);
return result;
}
/*
* Divide by the norm
*
* Returns false if value should not be indexed
*
* The caller needs to free the pointer stored in value
* if it's different than the original value
* Normalize value
*/
Datum
HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum value)
{
if (procinfo == NULL)
return DirectFunctionCall1(l2_normalize, value);
return FunctionCall1Coll(procinfo, collation, value);
}
/*
* Check if non-zero norm
*/
bool
HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, HnswType type)
HnswCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value)
{
double norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value));
if (norm > 0)
{
/* TODO Remove vector-specific code */
if (type == HNSW_TYPE_VECTOR)
{
Vector *v = DatumGetVector(*value);
Vector *result = InitVector(v->dim);
for (int i = 0; i < v->dim; i++)
result->x[i] = v->x[i] / norm;
*value = PointerGetDatum(result);
}
else if (type == HNSW_TYPE_HALFVEC)
{
HalfVector *v = DatumGetHalfVector(*value);
HalfVector *result = InitHalfVector(v->dim);
for (int i = 0; i < v->dim; i++)
result->x[i] = Float4ToHalfUnchecked(HalfToFloat4(v->x[i]) / norm);
*value = PointerGetDatum(result);
}
else if (type == HNSW_TYPE_SPARSEVEC)
{
SparseVector *v = DatumGetSparseVector(*value);
SparseVector *result = InitSparseVector(v->dim, v->nnz);
float *vx = SPARSEVEC_VALUES(v);
float *rx = SPARSEVEC_VALUES(result);
for (int i = 0; i < v->nnz; i++)
{
result->indices[i] = v->indices[i];
rx[i] = vx[i] / norm;
}
*value = PointerGetDatum(result);
}
else
elog(ERROR, "Unsupported type");
return true;
}
return false;
return DatumGetFloat8(FunctionCall1Coll(procinfo, collation, value)) > 0;
}
/*
@@ -1359,3 +1302,29 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
ep = w;
}
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_hnsw_support);
Datum
halfvec_hnsw_support(PG_FUNCTION_ARGS)
{
PG_RETURN_INT32(HNSW_TYPE_HALFVEC);
};
PGDLLEXPORT PG_FUNCTION_INFO_V1(bit_hnsw_support);
Datum
bit_hnsw_support(PG_FUNCTION_ARGS)
{
Oid typid = PG_GETARG_OID(0);
if (typid == BITOID)
PG_RETURN_INT32(HNSW_TYPE_BIT);
else
PG_RETURN_INT32(HNSW_TYPE_UNSUPPORTED);
};
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_hnsw_support);
Datum
sparsevec_hnsw_support(PG_FUNCTION_ARGS)
{
PG_RETURN_INT32(HNSW_TYPE_SPARSEVEC);
};

View File

@@ -6,6 +6,7 @@
#include "access/tableam.h"
#include "access/parallel.h"
#include "access/xact.h"
#include "bitvec.h"
#include "catalog/index.h"
#include "catalog/pg_operator_d.h"
#include "catalog/pg_type_d.h"
@@ -59,8 +60,10 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
*/
if (buildstate->kmeansnormprocinfo != NULL)
{
if (!IvfflatNormValue(buildstate->kmeansnormprocinfo, buildstate->collation, &value, buildstate->type))
if (!IvfflatCheckNorm(buildstate->kmeansnormprocinfo, buildstate->collation, value))
return;
value = IvfflatNormValue(buildstate->normalizeprocinfo, buildstate->collation, value);
}
if (samples->length < targsamples)
@@ -155,8 +158,10 @@ AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState
/* Normalize if needed */
if (buildstate->normprocinfo != NULL)
{
if (!IvfflatNormValue(buildstate->normprocinfo, buildstate->collation, &value, buildstate->type))
if (!IvfflatCheckNorm(buildstate->normprocinfo, buildstate->collation, value))
return;
value = IvfflatNormValue(buildstate->normalizeprocinfo, buildstate->collation, value);
}
/* Find the list that minimizes the distance */
@@ -324,6 +329,8 @@ GetMaxDimensions(IvfflatType type)
if (type == IVFFLAT_TYPE_HALFVEC)
maxDimensions *= 2;
else if (type == IVFFLAT_TYPE_BIT)
maxDimensions *= 32;
return maxDimensions;
}
@@ -338,6 +345,8 @@ GetItemSize(IvfflatType type, int dimensions)
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");
}
@@ -374,6 +383,7 @@ 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 */

View File

@@ -188,7 +188,7 @@ ivfflathandler(PG_FUNCTION_ARGS)
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
amroutine->amstrategies = 0;
amroutine->amsupport = 4;
amroutine->amsupport = 6;
#if PG_VERSION_NUM >= 130000
amroutine->amoptsprocnum = 0;
#endif

View File

@@ -28,6 +28,8 @@
#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_VERSION 1
#define IVFFLAT_MAGIC_NUMBER 0x14FF1A7
@@ -46,7 +48,9 @@
typedef enum IvfflatType
{
IVFFLAT_TYPE_VECTOR,
IVFFLAT_TYPE_HALFVEC
IVFFLAT_TYPE_HALFVEC,
IVFFLAT_TYPE_BIT,
IVFFLAT_TYPE_UNSUPPORTED
} IvfflatType;
/* Build phases */
@@ -174,6 +178,7 @@ typedef struct IvfflatBuildState
FmgrInfo *procinfo;
FmgrInfo *normprocinfo;
FmgrInfo *kmeansnormprocinfo;
FmgrInfo *normalizeprocinfo;
Oid collation;
/* Variables */
@@ -254,7 +259,9 @@ typedef struct IvfflatScanOpaqueData
/* Support functions */
FmgrInfo *procinfo;
FmgrInfo *normprocinfo;
FmgrInfo *normalizeprocinfo;
Oid collation;
Datum (*distfunc) (FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2);
/* Lists */
pairingheap *listQueue;
@@ -263,7 +270,7 @@ typedef struct IvfflatScanOpaqueData
typedef IvfflatScanOpaqueData * IvfflatScanOpaque;
#define VECTOR_ARRAY_SIZE(_length, _size) (sizeof(VectorArrayData) + (_length) * _size)
#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)
@@ -274,7 +281,8 @@ void VectorArrayFree(VectorArray arr);
void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatType type);
FmgrInfo *IvfflatOptionalProcInfo(Relation index, uint16 procnum);
IvfflatType IvfflatGetType(Relation index);
bool IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, IvfflatType type);
Datum IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum value);
bool IvfflatCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value);
int IvfflatGetLists(Relation index);
void IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions);
void IvfflatUpdateList(Relation index, ListInfo listInfo, BlockNumber insertPage, BlockNumber originalInsertPage, BlockNumber startPage, ForkNumber forkNum);

View File

@@ -85,8 +85,12 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, R
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
if (normprocinfo != NULL)
{
if (!IvfflatNormValue(normprocinfo, index->rd_indcollation[0], &value, IvfflatGetType(index)))
Oid collation = index->rd_indcollation[0];
if (!IvfflatCheckNorm(normprocinfo, collation, value))
return;
value = IvfflatNormValue(IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC), collation, value);
}
/* Find the insert page - sets the page and list info */

View File

@@ -3,10 +3,12 @@
#include <float.h>
#include <math.h>
#include "bitvec.h"
#include "halfutils.h"
#include "halfvec.h"
#include "ivfflat.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "vector.h"
@@ -134,6 +136,15 @@ 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
*/
@@ -151,6 +162,8 @@ QuickCenters(Relation index, VectorArray samples, VectorArray centers, IvfflatTy
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");
@@ -191,6 +204,16 @@ QuickCenters(Relation index, VectorArray samples, VectorArray centers, IvfflatTy
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");
@@ -263,6 +286,17 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
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");
@@ -308,6 +342,21 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
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));
}
}
/* Normalize if needed */
if (normprocinfo != NULL)
@@ -425,6 +474,18 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
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");
@@ -642,23 +703,26 @@ CheckCenters(Relation index, VectorArray centers, IvfflatType type)
elog(ERROR, "Infinite value detected. Please report a bug.");
}
}
else
else if (type != IVFFLAT_TYPE_BIT)
elog(ERROR, "Unsupported type");
}
/* 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 (type != IVFFLAT_TYPE_BIT)
{
if (datumIsEqual(PointerGetDatum(VectorArrayGet(centers, i)), PointerGetDatum(VectorArrayGet(centers, i - 1)), false, -1))
elog(ERROR, "Duplicate centers detected. Please report a bug.");
/* 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 */

View File

@@ -5,7 +5,6 @@
#include "access/relscan.h"
#include "catalog/pg_operator_d.h"
#include "catalog/pg_type_d.h"
#include "halfvec.h"
#include "lib/pairingheap.h"
#include "ivfflat.h"
#include "miscadmin.h"
@@ -57,7 +56,7 @@ GetScanLists(IndexScanDesc scan, Datum value)
double distance;
/* Use procinfo from the index instead of scan key for performance */
distance = DatumGetFloat8(FunctionCall2Coll(so->procinfo, so->collation, PointerGetDatum(&list->center), value));
distance = DatumGetFloat8(so->distfunc(so->procinfo, so->collation, PointerGetDatum(&list->center), value));
if (listCount < so->probes)
{
@@ -150,7 +149,7 @@ GetScanItems(IndexScanDesc scan, Datum value)
* performance
*/
ExecClearTuple(slot);
slot->tts_values[0] = FunctionCall2Coll(so->procinfo, so->collation, datum, value);
slot->tts_values[0] = so->distfunc(so->procinfo, so->collation, datum, value);
slot->tts_isnull[0] = false;
slot->tts_values[1] = PointerGetDatum(&itup->t_tid);
slot->tts_isnull[1] = false;
@@ -178,6 +177,15 @@ GetScanItems(IndexScanDesc scan, Datum value)
tuplesort_performsort(so->sortstate);
}
/*
* Zero distance
*/
static Datum
ZeroDistance(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
{
return Float8GetDatum(0.0);
}
/*
* Get scan value
*/
@@ -189,26 +197,21 @@ GetScanValue(IndexScanDesc scan)
if (scan->orderByData->sk_flags & SK_ISNULL)
{
IvfflatType type = IvfflatGetType(scan->indexRelation);
if (type == IVFFLAT_TYPE_VECTOR)
value = PointerGetDatum(InitVector(so->dimensions));
else if (type == IVFFLAT_TYPE_HALFVEC)
value = PointerGetDatum(InitHalfVector(so->dimensions));
else
elog(ERROR, "Unsupported type");
value = PointerGetDatum(NULL);
so->distfunc = ZeroDistance;
}
else
{
value = scan->orderByData->sk_argument;
so->distfunc = FunctionCall2Coll;
/* Value should not be compressed or toasted */
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
/* Fine if normalization fails */
/* Check normprocinfo since normalizeprocinfo not set for vector */
if (so->normprocinfo != NULL)
IvfflatNormValue(so->normprocinfo, so->collation, &value, IvfflatGetType(scan->indexRelation));
value = IvfflatNormValue(so->normalizeprocinfo, so->collation, value);
}
return value;
@@ -246,6 +249,7 @@ 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 */

View File

@@ -2,12 +2,9 @@
#include "access/generic_xlog.h"
#include "catalog/pg_type.h"
#include "halfutils.h"
#include "halfvec.h"
#include "fmgr.h"
#include "ivfflat.h"
#include "storage/bufmgr.h"
#include "vector.h"
#include "utils/syscache.h"
/*
* Allocate a vector array
@@ -17,6 +14,9 @@ VectorArrayInit(int maxlen, int dimensions, Size itemsize)
{
VectorArray res = palloc(sizeof(VectorArrayData));
/* Ensure items are aligned to prevent UB */
itemsize = MAXALIGN(itemsize);
res->length = 0;
res->maxlen = maxlen;
res->dim = dimensions;
@@ -67,73 +67,40 @@ IvfflatOptionalProcInfo(Relation index, uint16 procnum)
IvfflatType
IvfflatGetType(Relation index)
{
FmgrInfo *procinfo = IvfflatOptionalProcInfo(index, IVFFLAT_TYPE_SUPPORT_PROC);
Oid typid = TupleDescAttr(index->rd_att, 0)->atttypid;
HeapTuple tuple;
Form_pg_type type;
IvfflatType result;
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", typid);
if (procinfo == NULL)
return IVFFLAT_TYPE_VECTOR;
type = (Form_pg_type) GETSTRUCT(tuple);
if (strcmp(NameStr(type->typname), "vector") == 0)
result = IVFFLAT_TYPE_VECTOR;
else if (strcmp(NameStr(type->typname), "halfvec") == 0)
result = IVFFLAT_TYPE_HALFVEC;
else
{
ReleaseSysCache(tuple);
result = (IvfflatType) DatumGetInt32(FunctionCall1(procinfo, ObjectIdGetDatum(typid)));
if (result == IVFFLAT_TYPE_UNSUPPORTED)
elog(ERROR, "type not supported for ivfflat index");
}
ReleaseSysCache(tuple);
return result;
}
/*
* Divide by the norm
*
* Returns false if value should not be indexed
*
* The caller needs to free the pointer stored in value
* if it's different than the original value
* Normalize value
*/
Datum
IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum value)
{
if (procinfo == NULL)
return DirectFunctionCall1(l2_normalize, value);
return FunctionCall1Coll(procinfo, collation, value);
}
/*
* Check if non-zero norm
*/
bool
IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, IvfflatType type)
IvfflatCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value)
{
double norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value));
if (norm > 0)
{
if (type == IVFFLAT_TYPE_VECTOR)
{
Vector *v = DatumGetVector(*value);
Vector *result = InitVector(v->dim);
for (int i = 0; i < v->dim; i++)
result->x[i] = v->x[i] / norm;
*value = PointerGetDatum(result);
}
else if (type == IVFFLAT_TYPE_HALFVEC)
{
HalfVector *v = DatumGetHalfVector(*value);
HalfVector *result = InitHalfVector(v->dim);
for (int i = 0; i < v->dim; i++)
result->x[i] = Float4ToHalfUnchecked(HalfToFloat4(v->x[i]) / norm);
*value = PointerGetDatum(result);
}
else
elog(ERROR, "Unsupported type");
return true;
}
return false;
return DatumGetFloat8(FunctionCall1Coll(procinfo, collation, value)) > 0;
}
/*
@@ -278,3 +245,22 @@ IvfflatUpdateList(Relation index, ListInfo listInfo,
UnlockReleaseBuffer(buf);
}
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ivfflat_support);
Datum
halfvec_ivfflat_support(PG_FUNCTION_ARGS)
{
PG_RETURN_INT32(IVFFLAT_TYPE_HALFVEC);
};
PGDLLEXPORT PG_FUNCTION_INFO_V1(bit_ivfflat_support);
Datum
bit_ivfflat_support(PG_FUNCTION_ARGS)
{
Oid typid = PG_GETARG_OID(0);
if (typid == BITOID)
PG_RETURN_INT32(IVFFLAT_TYPE_BIT);
else
PG_RETURN_INT32(IVFFLAT_TYPE_UNSUPPORTED);
};

View File

@@ -4,6 +4,8 @@
#include <math.h>
#include "fmgr.h"
#include "halfutils.h"
#include "halfvec.h"
#include "libpq/pqformat.h"
#include "sparsevec.h"
#include "utils/array.h"
@@ -18,6 +20,12 @@
#include "utils/builtins.h"
#endif
typedef struct SparseInputElement
{
int32 index;
float value;
} SparseInputElement;
/*
* Ensure same dimensions
*/
@@ -164,6 +172,21 @@ sparsevec_isspace(char ch)
return false;
}
/*
* Compare indices
*/
static int
CompareIndices(const void *a, const void *b)
{
if (((SparseInputElement *) a)->index < ((SparseInputElement *) b)->index)
return -1;
if (((SparseInputElement *) a)->index > ((SparseInputElement *) b)->index)
return 1;
return 0;
}
/*
* Convert textual representation to internal representation
*/
@@ -178,8 +201,7 @@ sparsevec_in(PG_FUNCTION_ARGS)
char *stringEnd;
SparseVector *result;
float *rvalues;
int32 *indices;
float *values;
SparseInputElement *elements;
int maxNnz;
int nnz = 0;
@@ -197,8 +219,7 @@ sparsevec_in(PG_FUNCTION_ARGS)
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("sparsevec cannot have more than %d non-zero elements", SPARSEVEC_MAX_NNZ)));
indices = palloc(maxNnz * sizeof(int32));
values = palloc(maxNnz * sizeof(float));
elements = palloc(maxNnz * sizeof(SparseInputElement));
pt = lit;
@@ -291,8 +312,8 @@ sparsevec_in(PG_FUNCTION_ARGS)
/* Do not store zero values */
if (value != 0)
{
indices[nnz] = index;
values[nnz] = value;
elements[nnz].index = index;
elements[nnz].value = value;
nnz++;
}
@@ -353,12 +374,14 @@ sparsevec_in(PG_FUNCTION_ARGS)
CheckDim(dim);
CheckExpectedDim(typmod, dim);
qsort(elements, nnz, sizeof(SparseInputElement), CompareIndices);
result = InitSparseVector(dim, nnz);
rvalues = SPARSEVEC_VALUES(result);
for (int i = 0; i < nnz; i++)
{
result->indices[i] = indices[i];
rvalues[i] = values[i];
result->indices[i] = elements[i].index;
rvalues[i] = elements[i].value;
CheckIndex(result->indices, i, dim);
}
@@ -504,6 +527,10 @@ sparsevec_recv(PG_FUNCTION_ARGS)
{
values[i] = pq_getmsgfloat4(buf);
CheckElement(values[i]);
if (values[i] == 0)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("binary representation of sparsevec cannot contain zero values")));
}
PG_RETURN_POINTER(result);
@@ -579,7 +606,7 @@ vector_to_sparsevec(PG_FUNCTION_ARGS)
if (vec->x[i] != 0)
{
/* Safety check */
if (j == nnz)
if (j >= result->nnz)
elog(ERROR, "safety check failed");
result->indices[j] = i + 1;
@@ -591,15 +618,58 @@ vector_to_sparsevec(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(result);
}
/*
* Convert half vector to sparse vector
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_sparsevec);
Datum
halfvec_to_sparsevec(PG_FUNCTION_ARGS)
{
HalfVector *vec = PG_GETARG_HALFVEC_P(0);
int32 typmod = PG_GETARG_INT32(1);
SparseVector *result;
int dim = vec->dim;
int nnz = 0;
float *values;
int j = 0;
CheckDim(dim);
CheckExpectedDim(typmod, dim);
for (int i = 0; i < dim; i++)
{
if (!HalfIsZero(vec->x[i]))
nnz++;
}
result = InitSparseVector(dim, nnz);
values = SPARSEVEC_VALUES(result);
for (int i = 0; i < dim; i++)
{
if (!HalfIsZero(vec->x[i]))
{
/* Safety check */
if (j >= result->nnz)
elog(ERROR, "safety check failed");
result->indices[j] = i + 1;
values[j] = HalfToFloat4(vec->x[i]);
j++;
}
}
PG_RETURN_POINTER(result);
}
/*
* Get the L2 squared distance between sparse vectors
*/
static double
static float
SparsevecL2SquaredDistance(SparseVector * a, SparseVector * b)
{
float *ax = SPARSEVEC_VALUES(a);
float *bx = SPARSEVEC_VALUES(b);
double distance = 0.0;
float distance = 0.0;
int bpos = 0;
for (int i = 0; i < a->nnz; i++)
@@ -673,12 +743,12 @@ sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
/*
* Get the inner product of two sparse vectors
*/
static double
static float
SparsevecInnerProduct(SparseVector * a, SparseVector * b)
{
float *ax = SPARSEVEC_VALUES(a);
float *bx = SPARSEVEC_VALUES(b);
double distance = 0.0;
float distance = 0.0;
int bpos = 0;
for (int i = 0; i < a->nnz; i++)
@@ -781,12 +851,61 @@ sparsevec_cosine_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(1.0 - similarity);
}
/*
* Get the L1 distance between two sparse vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l1_distance);
Datum
sparsevec_l1_distance(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
float *ax = SPARSEVEC_VALUES(a);
float *bx = SPARSEVEC_VALUES(b);
double distance = 0.0;
int bpos = 0;
CheckDims(a, b);
for (int i = 0; i < a->nnz; i++)
{
int ai = a->indices[i];
int bi = -1;
for (int j = bpos; j < b->nnz; j++)
{
bi = b->indices[j];
if (ai == bi)
distance += fabsf(ax[i] - bx[j]);
else if (ai > bi)
distance += fabsf(bx[j]);
/* Update start for next iteration */
if (ai >= bi)
bpos = j + 1;
/* Found or passed it */
if (bi >= ai)
break;
}
if (ai != bi)
distance += fabsf(ax[i]);
}
for (int j = bpos; j < b->nnz; j++)
distance += fabsf(bx[j]);
PG_RETURN_FLOAT8(distance);
}
/*
* Get the L2 norm of a sparse vector
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_norm);
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_norm);
Datum
sparsevec_norm(PG_FUNCTION_ARGS)
sparsevec_l2_norm(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
float *ax = SPARSEVEC_VALUES(a);
@@ -798,3 +917,205 @@ sparsevec_norm(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(norm));
}
/*
* Normalize a sparse vector with the L2 norm
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_normalize);
Datum
sparsevec_l2_normalize(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
float *ax = SPARSEVEC_VALUES(a);
double norm = 0;
SparseVector *result;
float *rx;
result = InitSparseVector(a->dim, a->nnz);
rx = SPARSEVEC_VALUES(result);
/* Auto-vectorized */
for (int i = 0; i < a->nnz; i++)
norm += (double) ax[i] * (double) ax[i];
norm = sqrt(norm);
/* Return zero vector for zero norm */
if (norm > 0)
{
int zeros = 0;
for (int i = 0; i < a->nnz; i++)
{
result->indices[i] = a->indices[i];
rx[i] = ax[i] / norm;
if (isinf(rx[i]))
float_overflow_error();
if (rx[i] == 0)
zeros++;
}
/* Allocate a new vector in the unlikely event there are zeros */
if (zeros > 0)
{
SparseVector *newResult = InitSparseVector(result->dim, result->nnz - zeros);
float *nx = SPARSEVEC_VALUES(newResult);
int j = 0;
for (int i = 0; i < result->nnz; i++)
{
if (rx[i] == 0)
continue;
/* Safety check */
if (j >= newResult->nnz)
elog(ERROR, "safety check failed");
newResult->indices[j] = result->indices[i];
nx[j] = rx[i];
j++;
}
pfree(result);
PG_RETURN_POINTER(newResult);
}
}
PG_RETURN_POINTER(result);
}
/*
* Internal helper to compare sparse vectors
*/
static int
sparsevec_cmp_internal(SparseVector * a, SparseVector * b)
{
float *ax = SPARSEVEC_VALUES(a);
float *bx = SPARSEVEC_VALUES(b);
int nnz = Min(a->nnz, b->nnz);
/* Check values before dimensions to be consistent with Postgres arrays */
for (int i = 0; i < nnz; i++)
{
if (a->indices[i] < b->indices[i])
return ax[i] < 0 ? -1 : 1;
if (a->indices[i] > b->indices[i])
return bx[i] < 0 ? 1 : -1;
if (ax[i] < bx[i])
return -1;
if (ax[i] > bx[i])
return 1;
}
/* Check <= dim since indices start at 1 */
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)
return ax[nnz] < 0 ? -1 : 1;
if (a->dim < b->dim)
return -1;
if (a->dim > b->dim)
return 1;
return 0;
}
/*
* Less than
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_lt);
Datum
sparsevec_lt(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
PG_RETURN_BOOL(sparsevec_cmp_internal(a, b) < 0);
}
/*
* Less than or equal
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_le);
Datum
sparsevec_le(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
PG_RETURN_BOOL(sparsevec_cmp_internal(a, b) <= 0);
}
/*
* Equal
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_eq);
Datum
sparsevec_eq(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
PG_RETURN_BOOL(sparsevec_cmp_internal(a, b) == 0);
}
/*
* Not equal
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_ne);
Datum
sparsevec_ne(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
PG_RETURN_BOOL(sparsevec_cmp_internal(a, b) != 0);
}
/*
* Greater than or equal
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_ge);
Datum
sparsevec_ge(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
PG_RETURN_BOOL(sparsevec_cmp_internal(a, b) >= 0);
}
/*
* Greater than
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_gt);
Datum
sparsevec_gt(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
PG_RETURN_BOOL(sparsevec_cmp_internal(a, b) > 0);
}
/*
* Compare sparse vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_cmp);
Datum
sparsevec_cmp(PG_FUNCTION_ARGS)
{
SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
PG_RETURN_INT32(sparsevec_cmp_internal(a, b));
}

View File

@@ -1,6 +1,8 @@
#ifndef SPARSEVEC_H
#define SPARSEVEC_H
#include "fmgr.h"
#define SPARSEVEC_MAX_DIM 100000
#define SPARSEVEC_MAX_NNZ 16000

View File

@@ -2,7 +2,8 @@
#include <math.h>
#include "bitvector.h"
#include "bitutils.h"
#include "bitvec.h"
#include "catalog/pg_type.h"
#include "common/shortest_dec.h"
#include "fmgr.h"
@@ -33,11 +34,10 @@
#define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1)
#define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1))
/* target_clones requires glibc */
#if defined(__x86_64__) && defined(__gnu_linux__) && defined(__has_attribute) && __has_attribute(target_clones) && !defined(__FMA__)
#define VECTOR_DISPATCH __attribute__((target_clones("default", "fma")))
#if defined(USE_TARGET_CLONES) && !defined(__FMA__)
#define VECTOR_TARGET_CLONES __attribute__((target_clones("default", "fma")))
#else
#define VECTOR_DISPATCH
#define VECTOR_TARGET_CLONES
#endif
PG_MODULE_MAGIC;
@@ -49,6 +49,7 @@ PGDLLEXPORT void _PG_init(void);
void
_PG_init(void)
{
BitvecInit();
HalfvecInit();
HnswInit();
IvfflatInit();
@@ -564,7 +565,7 @@ halfvec_to_vector(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(result);
}
VECTOR_DISPATCH static float
VECTOR_TARGET_CLONES static float
VectorL2SquaredDistance(int dim, float *ax, float *bx)
{
float distance = 0.0;
@@ -611,7 +612,7 @@ vector_l2_squared_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8((double) VectorL2SquaredDistance(a->dim, a->x, b->x));
}
VECTOR_DISPATCH static float
VECTOR_TARGET_CLONES static float
VectorInnerProduct(int dim, float *ax, float *bx)
{
float distance = 0.0;
@@ -653,6 +654,25 @@ vector_negative_inner_product(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8((double) -VectorInnerProduct(a->dim, a->x, b->x));
}
VECTOR_TARGET_CLONES static double
VectorCosineSimilarity(int dim, float *ax, float *bx)
{
float similarity = 0.0;
float norma = 0.0;
float normb = 0.0;
/* Auto-vectorized */
for (int i = 0; i < dim; i++)
{
similarity += ax[i] * bx[i];
norma += ax[i] * ax[i];
normb += bx[i] * bx[i];
}
/* Use sqrt(a * b) over sqrt(a) * sqrt(b) */
return (double) similarity / sqrt((double) norma * (double) normb);
}
/*
* Get the cosine distance between two vectors
*/
@@ -662,25 +682,11 @@ cosine_distance(PG_FUNCTION_ARGS)
{
Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1);
float *ax = a->x;
float *bx = b->x;
float distance = 0.0;
float norma = 0.0;
float normb = 0.0;
double similarity;
CheckDims(a, b);
/* Auto-vectorized */
for (int i = 0; i < a->dim; i++)
{
distance += ax[i] * bx[i];
norma += ax[i] * ax[i];
normb += bx[i] * bx[i];
}
/* Use sqrt(a * b) over sqrt(a) * sqrt(b) */
similarity = (double) distance / sqrt((double) norma * (double) normb);
similarity = VectorCosineSimilarity(a->dim, a->x, b->x);
#ifdef _MSC_VER
/* /fp:fast may not propagate NaN */
@@ -723,6 +729,19 @@ vector_spherical_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(acos(distance) / M_PI);
}
/* Does not require FMA, but keep logic simple */
VECTOR_TARGET_CLONES static float
VectorL1Distance(int dim, float *ax, float *bx)
{
float distance = 0.0;
/* Auto-vectorized */
for (int i = 0; i < dim; i++)
distance += fabsf(ax[i] - bx[i]);
return distance;
}
/*
* Get the L1 distance between two vectors
*/
@@ -732,17 +751,10 @@ l1_distance(PG_FUNCTION_ARGS)
{
Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1);
float *ax = a->x;
float *bx = b->x;
float distance = 0.0;
CheckDims(a, b);
/* Auto-vectorized */
for (int i = 0; i < a->dim; i++)
distance += fabsf(ax[i] - bx[i]);
PG_RETURN_FLOAT8((double) distance);
PG_RETURN_FLOAT8((double) VectorL1Distance(a->dim, a->x, b->x));
}
/*
@@ -775,6 +787,45 @@ vector_norm(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(norm));
}
/*
* Normalize a vector with the L2 norm
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(l2_normalize);
Datum
l2_normalize(PG_FUNCTION_ARGS)
{
Vector *a = PG_GETARG_VECTOR_P(0);
float *ax = a->x;
double norm = 0;
Vector *result;
float *rx;
result = InitVector(a->dim);
rx = result->x;
/* Auto-vectorized */
for (int i = 0; i < a->dim; i++)
norm += (double) ax[i] * (double) ax[i];
norm = sqrt(norm);
/* Return zero vector for zero norm */
if (norm > 0)
{
for (int i = 0; i < a->dim; i++)
rx[i] = ax[i] / norm;
/* Check for overflow */
for (int i = 0; i < a->dim; i++)
{
if (isinf(rx[i]))
float_overflow_error();
}
}
PG_RETURN_POINTER(result);
}
/*
* Add vectors
*/
@@ -877,12 +928,36 @@ vector_mul(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(result);
}
/*
* Concatenate vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_concat);
Datum
vector_concat(PG_FUNCTION_ARGS)
{
Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1);
Vector *result;
int dim = a->dim + b->dim;
CheckDim(dim);
result = InitVector(dim);
for (int i = 0; i < a->dim; i++)
result->x[i] = a->x[i];
for (int i = 0; i < b->dim; i++)
result->x[i + a->dim] = b->x[i];
PG_RETURN_POINTER(result);
}
/*
* Quantize a vector
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(quantize_binary);
PGDLLEXPORT PG_FUNCTION_INFO_V1(binary_quantize);
Datum
quantize_binary(PG_FUNCTION_ARGS)
binary_quantize(PG_FUNCTION_ARGS)
{
Vector *a = PG_GETARG_VECTOR_P(0);
float *ax = a->x;
@@ -1107,7 +1182,7 @@ vector_accum(PG_FUNCTION_ARGS)
}
/*
* Combine vectors
* Combine vectors or half vectors
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_combine);
Datum

View File

@@ -1,6 +1,8 @@
#ifndef VECTOR_H
#define VECTOR_H
#include "fmgr.h"
#define VECTOR_MAX_DIM 16000
#define VECTOR_SIZE(_dim) (offsetof(Vector, x) + sizeof(float)*(_dim))
@@ -19,5 +21,6 @@ typedef struct 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);
#endif

View File

@@ -28,6 +28,24 @@ SELECT hamming_distance('10101010101010101010', '01010101010101010101');
20
(1 row)
SELECT hamming_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101');
hamming_distance
------------------
0
(1 row)
SELECT hamming_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010');
hamming_distance
------------------
513
(1 row)
SELECT hamming_distance('110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011', '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001');
hamming_distance
------------------
2
(1 row)
SELECT hamming_distance('', '');
hamming_distance
------------------
@@ -86,6 +104,24 @@ SELECT jaccard_distance('10101010101010101010', '01010101010101010101');
1
(1 row)
SELECT jaccard_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101');
jaccard_distance
------------------
0
(1 row)
SELECT jaccard_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010');
jaccard_distance
------------------
1
(1 row)
SELECT jaccard_distance('110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011', '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001');
jaccard_distance
------------------
0.5
(1 row)
SELECT jaccard_distance('', '');
jaccard_distance
------------------

View File

@@ -0,0 +1,20 @@
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;

View File

@@ -0,0 +1,20 @@
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;

View File

@@ -8,10 +8,13 @@ SELECT * FROM t WHERE val = '[1,2,3]';
[1,2,3]
(1 row)
SELECT * FROM t ORDER BY val LIMIT 1;
SELECT * FROM t ORDER BY val;
val
---------
[0,0,0]
(1 row)
[1,1,1]
[1,2,3]
(4 rows)
DROP TABLE t;

View File

@@ -170,6 +170,44 @@ SELECT '{2:1.5,4:3.5}/5'::sparsevec::vector(4);
ERROR: expected 4 dimensions, not 5
SELECT '{}/16001'::sparsevec::vector;
ERROR: vector cannot have more than 16000 dimensions
SELECT '[0,1.5,0,3.5,0]'::halfvec::sparsevec;
sparsevec
-----------------
{2:1.5,4:3.5}/5
(1 row)
SELECT '[0,1.5,0,3.5,0]'::halfvec::sparsevec(5);
sparsevec
-----------------
{2:1.5,4:3.5}/5
(1 row)
SELECT '[0,1.5,0,3.5,0]'::halfvec::sparsevec(4);
ERROR: expected 4 dimensions, not 5
SELECT '{2:1.5,4:3.5}/5'::sparsevec::halfvec;
halfvec
-----------------
[0,1.5,0,3.5,0]
(1 row)
SELECT '{2:1.5,4:3.5}/5'::sparsevec::halfvec(5);
halfvec
-----------------
[0,1.5,0,3.5,0]
(1 row)
SELECT '{2:1.5,4:3.5}/5'::sparsevec::halfvec(4);
ERROR: expected 4 dimensions, not 5
SELECT '{}/16001'::sparsevec::halfvec;
ERROR: halfvec cannot have more than 16000 dimensions
SELECT '{1:65520}/1'::sparsevec::halfvec;
ERROR: "65520" is out of range for type halfvec
SELECT '{1:1e-8}/1'::sparsevec::halfvec;
halfvec
---------
[0]
(1 row)
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
ERROR: vector cannot have more than 16000 dimensions
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;

View File

@@ -1,19 +1,179 @@
SELECT round(halfvec_norm('[1,1]')::numeric, 5);
SELECT '[1,2,3]'::halfvec + '[4,5,6]';
?column?
----------
[5,7,9]
(1 row)
SELECT '[65519]'::halfvec + '[65519]';
ERROR: value out of range: overflow
SELECT '[1,2]'::halfvec + '[3]';
ERROR: different halfvec dimensions 2 and 1
SELECT '[1,2,3]'::halfvec - '[4,5,6]';
?column?
------------
[-3,-3,-3]
(1 row)
SELECT '[-65519]'::halfvec - '[65519]';
ERROR: value out of range: overflow
SELECT '[1,2]'::halfvec - '[3]';
ERROR: different halfvec dimensions 2 and 1
SELECT '[1,2,3]'::halfvec * '[4,5,6]';
?column?
-----------
[4,10,18]
(1 row)
SELECT '[65519]'::halfvec * '[65519]';
ERROR: value out of range: overflow
SELECT '[1e-7]'::halfvec * '[1e-7]';
ERROR: value out of range: underflow
SELECT '[1,2]'::halfvec * '[3]';
ERROR: different halfvec dimensions 2 and 1
SELECT '[1,2,3]'::halfvec || '[4,5]';
?column?
-------------
[1,2,3,4,5]
(1 row)
SELECT array_fill(0, ARRAY[16000])::halfvec || '[1]';
ERROR: halfvec cannot have more than 16000 dimensions
SELECT '[1,2,3]'::halfvec < '[1,2,3]';
?column?
----------
f
(1 row)
SELECT '[1,2,3]'::halfvec < '[1,2]';
?column?
----------
f
(1 row)
SELECT '[1,2,3]'::halfvec <= '[1,2,3]';
?column?
----------
t
(1 row)
SELECT '[1,2,3]'::halfvec <= '[1,2]';
?column?
----------
f
(1 row)
SELECT '[1,2,3]'::halfvec = '[1,2,3]';
?column?
----------
t
(1 row)
SELECT '[1,2,3]'::halfvec = '[1,2]';
?column?
----------
f
(1 row)
SELECT '[1,2,3]'::halfvec != '[1,2,3]';
?column?
----------
f
(1 row)
SELECT '[1,2,3]'::halfvec != '[1,2]';
?column?
----------
t
(1 row)
SELECT '[1,2,3]'::halfvec >= '[1,2,3]';
?column?
----------
t
(1 row)
SELECT '[1,2,3]'::halfvec >= '[1,2]';
?column?
----------
t
(1 row)
SELECT '[1,2,3]'::halfvec > '[1,2,3]';
?column?
----------
f
(1 row)
SELECT '[1,2,3]'::halfvec > '[1,2]';
?column?
----------
t
(1 row)
SELECT halfvec_cmp('[1,2,3]', '[1,2,3]');
halfvec_cmp
-------------
0
(1 row)
SELECT halfvec_cmp('[1,2,3]', '[0,0,0]');
halfvec_cmp
-------------
1
(1 row)
SELECT halfvec_cmp('[0,0,0]', '[1,2,3]');
halfvec_cmp
-------------
-1
(1 row)
SELECT halfvec_cmp('[1,2]', '[1,2,3]');
halfvec_cmp
-------------
-1
(1 row)
SELECT halfvec_cmp('[1,2,3]', '[1,2]');
halfvec_cmp
-------------
1
(1 row)
SELECT halfvec_cmp('[1,2]', '[2,3,4]');
halfvec_cmp
-------------
-1
(1 row)
SELECT halfvec_cmp('[2,3]', '[1,2,3]');
halfvec_cmp
-------------
1
(1 row)
SELECT vector_dims('[1,2,3]'::halfvec);
vector_dims
-------------
3
(1 row)
SELECT round(l2_norm('[1,1]'::halfvec)::numeric, 5);
round
---------
1.41421
(1 row)
SELECT halfvec_norm('[3,4]');
halfvec_norm
--------------
5
SELECT l2_norm('[3,4]'::halfvec);
l2_norm
---------
5
(1 row)
SELECT halfvec_norm('[0,1]');
halfvec_norm
--------------
1
SELECT l2_norm('[0,1]'::halfvec);
l2_norm
---------
1
(1 row)
SELECT l2_distance('[0,0]'::halfvec, '[3,4]');
@@ -112,6 +272,18 @@ SELECT cosine_distance('[1,1]'::halfvec, '[-1.1,-1.1]');
2
(1 row)
SELECT cosine_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[1,2,3,4,5,6,7,8,9]');
cosine_distance
-----------------
0
(1 row)
SELECT cosine_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[-1,-2,-3,-4,-5,-6,-7,-8,-9]');
cosine_distance
-----------------
2
(1 row)
SELECT '[1,2]'::halfvec <=> '[2,4]';
?column?
----------
@@ -132,14 +304,62 @@ SELECT l1_distance('[0,0]'::halfvec, '[0,1]');
SELECT l1_distance('[1,2]'::halfvec, '[3]');
ERROR: different halfvec dimensions 2 and 1
SELECT quantize_binary('[1,0,-1]'::halfvec);
quantize_binary
SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[1,2,3,4,5,6,7,8,9]');
l1_distance
-------------
0
(1 row)
SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[0,3,2,5,4,7,6,9,8]');
l1_distance
-------------
9
(1 row)
SELECT '[0,0]'::halfvec <+> '[3,4]';
?column?
----------
7
(1 row)
SELECT l2_normalize('[3,4]'::halfvec);
l2_normalize
------------------------
[0.60009766,0.7998047]
(1 row)
SELECT l2_normalize('[3,0]'::halfvec);
l2_normalize
--------------
[1,0]
(1 row)
SELECT l2_normalize('[0,0.1]'::halfvec);
l2_normalize
--------------
[0,1]
(1 row)
SELECT l2_normalize('[0,0]'::halfvec);
l2_normalize
--------------
[0,0]
(1 row)
SELECT l2_normalize('[65504]'::halfvec);
l2_normalize
--------------
[1]
(1 row)
SELECT binary_quantize('[1,0,-1]'::halfvec);
binary_quantize
-----------------
100
(1 row)
SELECT quantize_binary('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::halfvec);
quantize_binary
SELECT binary_quantize('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::halfvec);
binary_quantize
-----------------
01001110101
(1 row)
@@ -174,3 +394,53 @@ 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 avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]']) v;
avg
-----------
[2,3.5,5]
(1 row)
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]', NULL]) v;
avg
-----------
[2,3.5,5]
(1 row)
SELECT avg(v) FROM unnest(ARRAY[]::halfvec[]) v;
avg
-----
(1 row)
SELECT avg(v) FROM unnest(ARRAY['[1,2]'::halfvec, '[3]']) v;
ERROR: expected 2 dimensions, not 1
SELECT avg(v) FROM unnest(ARRAY['[65504]'::halfvec, '[65504]']) v;
avg
---------
[65504]
(1 row)
SELECT halfvec_avg(array_agg(n)) FROM generate_series(1, 16002) n;
ERROR: halfvec cannot have more than 16000 dimensions
SELECT sum(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]']) v;
sum
----------
[4,7,10]
(1 row)
SELECT sum(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]', NULL]) v;
sum
----------
[4,7,10]
(1 row)
SELECT sum(v) FROM unnest(ARRAY[]::halfvec[]) v;
sum
-----
(1 row)
SELECT sum(v) FROM unnest(ARRAY['[1,2]'::halfvec, '[3]']) v;
ERROR: different halfvec dimensions 2 and 1
SELECT sum(v) FROM unnest(ARRAY['[65504]'::halfvec, '[65504]']) v;
ERROR: value out of range: overflow

View File

@@ -0,0 +1,21 @@
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;

View File

@@ -0,0 +1,21 @@
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;

View File

@@ -0,0 +1,21 @@
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;

View File

@@ -0,0 +1,36 @@
SET enable_seqscan = off;
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);
INSERT INTO t (val) VALUES (B'110');
SELECT * FROM t ORDER BY val <~> B'111';
val
-----
111
110
100
000
(4 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <~> (SELECT NULL::bit)) t2;
count
-------
4
(1 row)
DROP TABLE t;
-- TODO move
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
CREATE INDEX ON t USING ivfflat ((val::bit(3)) bit_hamming_ops) WITH (lists = 1);
NOTICE: ivfflat index created with little data
DETAIL: This will cause low recall.
HINT: Drop the index until the table has more data.
CREATE INDEX ON t USING ivfflat ((val::bit(64001)) bit_hamming_ops) WITH (lists = 1);
ERROR: column cannot have more than 64000 dimensions for ivfflat index
CREATE INDEX ON t USING ivfflat ((val::bit(2)) bit_hamming_ops) WITH (lists = 5);
NOTICE: ivfflat index created with little data
DETAIL: This will cause low recall.
HINT: Drop the index until the table has more data.
DROP TABLE t;

View File

@@ -1,25 +1,139 @@
SELECT round(sparsevec_norm('{1:1,2:1}/2')::numeric, 5);
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 sparsevec_norm('{1:3,2:4}/2');
sparsevec_norm
----------------
5
SELECT l2_norm('{1:3,2:4}/2'::sparsevec);
l2_norm
---------
5
(1 row)
SELECT sparsevec_norm('{2:1}/2');
sparsevec_norm
----------------
1
SELECT l2_norm('{2:1}/2'::sparsevec);
l2_norm
---------
1
(1 row)
SELECT sparsevec_norm('{1:3e37,2:4e37}/2')::real;
sparsevec_norm
----------------
5e+37
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');
@@ -28,6 +142,18 @@ SELECT l2_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
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
-------------
@@ -46,10 +172,24 @@ SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
10
(1 row)
SELECT sparsevec_negative_inner_product('{1:1,2:2}/2', '{1:2,2:4}/2');
sparsevec_negative_inner_product
----------------------------------
-10
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');
@@ -64,6 +204,18 @@ SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{}/2');
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
-----------------
@@ -76,11 +228,109 @@ SELECT cosine_distance('{1:2}/2'::sparsevec, '{2:2}/2');
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 cosine_distance('{1:2}/2'::sparsevec, '{1:1}/3');
ERROR: different sparsevec dimensions 2 and 3
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)

View File

@@ -164,8 +164,18 @@ SELECT '{1:0,2:1,3:0}/3'::sparsevec;
(1 row)
SELECT '{2:1,1:1}/2'::sparsevec;
ERROR: indexes must be in ascending order
LINE 1: 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

View File

@@ -6,6 +6,8 @@ SELECT '[1,2,3]'::vector + '[4,5,6]';
SELECT '[3e38]'::vector + '[3e38]';
ERROR: value out of range: overflow
SELECT '[1,2]'::vector + '[3]';
ERROR: different vector dimensions 2 and 1
SELECT '[1,2,3]'::vector - '[4,5,6]';
?column?
------------
@@ -14,6 +16,8 @@ SELECT '[1,2,3]'::vector - '[4,5,6]';
SELECT '[-3e38]'::vector - '[3e38]';
ERROR: value out of range: overflow
SELECT '[1,2]'::vector - '[3]';
ERROR: different vector dimensions 2 and 1
SELECT '[1,2,3]'::vector * '[4,5,6]';
?column?
-----------
@@ -24,6 +28,16 @@ SELECT '[1e37]'::vector * '[1e37]';
ERROR: value out of range: overflow
SELECT '[1e-37]'::vector * '[1e-37]';
ERROR: value out of range: underflow
SELECT '[1,2]'::vector * '[3]';
ERROR: different vector dimensions 2 and 1
SELECT '[1,2,3]'::vector || '[4,5]';
?column?
-------------
[1,2,3,4,5]
(1 row)
SELECT array_fill(0, ARRAY[16000])::vector || '[1]';
ERROR: vector cannot have more than 16000 dimensions
SELECT '[1,2,3]'::vector < '[1,2,3]';
?column?
----------
@@ -138,7 +152,7 @@ SELECT vector_cmp('[2,3]', '[1,2,3]');
1
(1 row)
SELECT vector_dims('[1,2,3]');
SELECT vector_dims('[1,2,3]'::vector);
vector_dims
-------------
3
@@ -188,6 +202,18 @@ SELECT l2_distance('[3e38]'::vector, '[-3e38]');
Infinity
(1 row)
SELECT l2_distance('[1,1,1,1,1,1,1,1,1]'::vector, '[1,1,1,1,1,1,1,4,5]');
l2_distance
-------------
5
(1 row)
SELECT '[0,0]'::vector <-> '[3,4]';
?column?
----------
5
(1 row)
SELECT inner_product('[1,2]'::vector, '[3,4]');
inner_product
---------------
@@ -202,6 +228,18 @@ SELECT inner_product('[3e38]'::vector, '[3e38]');
Infinity
(1 row)
SELECT inner_product('[1,1,1,1,1,1,1,1,1]'::vector, '[1,2,3,4,5,6,7,8,9]');
inner_product
---------------
45
(1 row)
SELECT '[1,2]'::vector <#> '[3,4]';
?column?
----------
-11
(1 row)
SELECT cosine_distance('[1,2]'::vector, '[2,4]');
cosine_distance
-----------------
@@ -252,6 +290,24 @@ SELECT cosine_distance('[3e38]'::vector, '[3e38]');
NaN
(1 row)
SELECT cosine_distance('[1,2,3,4,5,6,7,8,9]'::vector, '[1,2,3,4,5,6,7,8,9]');
cosine_distance
-----------------
0
(1 row)
SELECT cosine_distance('[1,2,3,4,5,6,7,8,9]'::vector, '[-1,-2,-3,-4,-5,-6,-7,-8,-9]');
cosine_distance
-----------------
2
(1 row)
SELECT '[1,2]'::vector <=> '[2,4]';
?column?
----------
0
(1 row)
SELECT l1_distance('[0,0]'::vector, '[3,4]');
l1_distance
-------------
@@ -272,14 +328,62 @@ SELECT l1_distance('[3e38]'::vector, '[-3e38]');
Infinity
(1 row)
SELECT quantize_binary('[1,0,-1]'::vector);
quantize_binary
SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::vector, '[1,2,3,4,5,6,7,8,9]');
l1_distance
-------------
0
(1 row)
SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::vector, '[0,3,2,5,4,7,6,9,8]');
l1_distance
-------------
9
(1 row)
SELECT '[0,0]'::vector <+> '[3,4]';
?column?
----------
7
(1 row)
SELECT l2_normalize('[3,4]'::vector);
l2_normalize
--------------
[0.6,0.8]
(1 row)
SELECT l2_normalize('[3,0]'::vector);
l2_normalize
--------------
[1,0]
(1 row)
SELECT l2_normalize('[0,0.1]'::vector);
l2_normalize
--------------
[0,1]
(1 row)
SELECT l2_normalize('[0,0]'::vector);
l2_normalize
--------------
[0,0]
(1 row)
SELECT l2_normalize('[3e38]'::vector);
l2_normalize
--------------
[1]
(1 row)
SELECT binary_quantize('[1,0,-1]'::vector);
binary_quantize
-----------------
100
(1 row)
SELECT quantize_binary('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::vector);
quantize_binary
SELECT binary_quantize('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::vector);
binary_quantize
-----------------
01001110101
(1 row)

View File

@@ -3,6 +3,9 @@ SELECT hamming_distance('111', '110');
SELECT hamming_distance('111', '100');
SELECT hamming_distance('111', '000');
SELECT hamming_distance('10101010101010101010', '01010101010101010101');
SELECT hamming_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101');
SELECT hamming_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010');
SELECT hamming_distance('110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011', '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001');
SELECT hamming_distance('', '');
SELECT hamming_distance('111', '00');
SELECT hamming_distance('111', '000'::varbit(4));
@@ -15,6 +18,9 @@ SELECT jaccard_distance('1111', '1000');
SELECT jaccard_distance('1111', '0000');
SELECT jaccard_distance('1100', '1000');
SELECT jaccard_distance('10101010101010101010', '01010101010101010101');
SELECT jaccard_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101');
SELECT jaccard_distance('101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101', '010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010');
SELECT jaccard_distance('110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011', '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001');
SELECT jaccard_distance('', '');
SELECT jaccard_distance('1111', '000');
SELECT jaccard_distance('1111', '0000'::varbit(5));

View File

@@ -0,0 +1,10 @@
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;

View File

@@ -0,0 +1,10 @@
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;

View File

@@ -5,6 +5,6 @@ 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 LIMIT 1;
SELECT * FROM t ORDER BY val;
DROP TABLE t;

View File

@@ -47,6 +47,17 @@ SELECT '{2:1.5,4:3.5}/5'::sparsevec::vector(5);
SELECT '{2:1.5,4:3.5}/5'::sparsevec::vector(4);
SELECT '{}/16001'::sparsevec::vector;
SELECT '[0,1.5,0,3.5,0]'::halfvec::sparsevec;
SELECT '[0,1.5,0,3.5,0]'::halfvec::sparsevec(5);
SELECT '[0,1.5,0,3.5,0]'::halfvec::sparsevec(4);
SELECT '{2:1.5,4:3.5}/5'::sparsevec::halfvec;
SELECT '{2:1.5,4:3.5}/5'::sparsevec::halfvec(5);
SELECT '{2:1.5,4:3.5}/5'::sparsevec::halfvec(4);
SELECT '{}/16001'::sparsevec::halfvec;
SELECT '{1:65520}/1'::sparsevec::halfvec;
SELECT '{1:1e-8}/1'::sparsevec::halfvec;
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;

View File

@@ -1,6 +1,45 @@
SELECT round(halfvec_norm('[1,1]')::numeric, 5);
SELECT halfvec_norm('[3,4]');
SELECT halfvec_norm('[0,1]');
SELECT '[1,2,3]'::halfvec + '[4,5,6]';
SELECT '[65519]'::halfvec + '[65519]';
SELECT '[1,2]'::halfvec + '[3]';
SELECT '[1,2,3]'::halfvec - '[4,5,6]';
SELECT '[-65519]'::halfvec - '[65519]';
SELECT '[1,2]'::halfvec - '[3]';
SELECT '[1,2,3]'::halfvec * '[4,5,6]';
SELECT '[65519]'::halfvec * '[65519]';
SELECT '[1e-7]'::halfvec * '[1e-7]';
SELECT '[1,2]'::halfvec * '[3]';
SELECT '[1,2,3]'::halfvec || '[4,5]';
SELECT array_fill(0, ARRAY[16000])::halfvec || '[1]';
SELECT '[1,2,3]'::halfvec < '[1,2,3]';
SELECT '[1,2,3]'::halfvec < '[1,2]';
SELECT '[1,2,3]'::halfvec <= '[1,2,3]';
SELECT '[1,2,3]'::halfvec <= '[1,2]';
SELECT '[1,2,3]'::halfvec = '[1,2,3]';
SELECT '[1,2,3]'::halfvec = '[1,2]';
SELECT '[1,2,3]'::halfvec != '[1,2,3]';
SELECT '[1,2,3]'::halfvec != '[1,2]';
SELECT '[1,2,3]'::halfvec >= '[1,2,3]';
SELECT '[1,2,3]'::halfvec >= '[1,2]';
SELECT '[1,2,3]'::halfvec > '[1,2,3]';
SELECT '[1,2,3]'::halfvec > '[1,2]';
SELECT halfvec_cmp('[1,2,3]', '[1,2,3]');
SELECT halfvec_cmp('[1,2,3]', '[0,0,0]');
SELECT halfvec_cmp('[0,0,0]', '[1,2,3]');
SELECT halfvec_cmp('[1,2]', '[1,2,3]');
SELECT halfvec_cmp('[1,2,3]', '[1,2]');
SELECT halfvec_cmp('[1,2]', '[2,3,4]');
SELECT halfvec_cmp('[2,3]', '[1,2,3]');
SELECT 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_distance('[0,0]'::halfvec, '[3,4]');
SELECT l2_distance('[0,0]'::halfvec, '[0,1]');
@@ -22,14 +61,25 @@ SELECT cosine_distance('[1,1]'::halfvec, '[-1,-1]');
SELECT cosine_distance('[1,2]'::halfvec, '[3]');
SELECT cosine_distance('[1,1]'::halfvec, '[1.1,1.1]');
SELECT cosine_distance('[1,1]'::halfvec, '[-1.1,-1.1]');
SELECT cosine_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[1,2,3,4,5,6,7,8,9]');
SELECT cosine_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[-1,-2,-3,-4,-5,-6,-7,-8,-9]');
SELECT '[1,2]'::halfvec <=> '[2,4]';
SELECT l1_distance('[0,0]'::halfvec, '[3,4]');
SELECT l1_distance('[0,0]'::halfvec, '[0,1]');
SELECT l1_distance('[1,2]'::halfvec, '[3]');
SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[1,2,3,4,5,6,7,8,9]');
SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[0,3,2,5,4,7,6,9,8]');
SELECT '[0,0]'::halfvec <+> '[3,4]';
SELECT quantize_binary('[1,0,-1]'::halfvec);
SELECT quantize_binary('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::halfvec);
SELECT l2_normalize('[3,4]'::halfvec);
SELECT l2_normalize('[3,0]'::halfvec);
SELECT l2_normalize('[0,0.1]'::halfvec);
SELECT l2_normalize('[0,0]'::halfvec);
SELECT l2_normalize('[65504]'::halfvec);
SELECT binary_quantize('[1,0,-1]'::halfvec);
SELECT binary_quantize('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::halfvec);
SELECT subvector('[1,2,3,4,5]'::halfvec, 1, 3);
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 2);
@@ -38,3 +88,16 @@ 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 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;
SELECT avg(v) FROM unnest(ARRAY[]::halfvec[]) v;
SELECT avg(v) FROM unnest(ARRAY['[1,2]'::halfvec, '[3]']) v;
SELECT avg(v) FROM unnest(ARRAY['[65504]'::halfvec, '[65504]']) v;
SELECT halfvec_avg(array_agg(n)) FROM generate_series(1, 16002) n;
SELECT sum(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]']) v;
SELECT sum(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]', NULL]) v;
SELECT sum(v) FROM unnest(ARRAY[]::halfvec[]) v;
SELECT sum(v) FROM unnest(ARRAY['[1,2]'::halfvec, '[3]']) v;
SELECT sum(v) FROM unnest(ARRAY['[65504]'::halfvec, '[65504]']) v;

View File

@@ -0,0 +1,12 @@
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;

View File

@@ -0,0 +1,12 @@
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';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::sparsevec)) t2;
DROP TABLE t;

View File

@@ -0,0 +1,12 @@
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]';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::vector)) t2;
DROP TABLE t;

View File

@@ -0,0 +1,20 @@
SET enable_seqscan = off;
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);
INSERT INTO t (val) VALUES (B'110');
SELECT * FROM t ORDER BY val <~> B'111';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <~> (SELECT NULL::bit)) t2;
DROP TABLE t;
-- TODO move
CREATE TABLE t (val varbit(3));
CREATE INDEX ON t USING ivfflat (val bit_hamming_ops) WITH (lists = 1);
CREATE INDEX ON t USING ivfflat ((val::bit(3)) bit_hamming_ops) WITH (lists = 1);
CREATE INDEX ON t USING ivfflat ((val::bit(64001)) bit_hamming_ops) WITH (lists = 1);
CREATE INDEX ON t USING ivfflat ((val::bit(2)) bit_hamming_ops) WITH (lists = 5);
DROP TABLE t;

View File

@@ -1,18 +1,65 @@
SELECT round(sparsevec_norm('{1:1,2:1}/2')::numeric, 5);
SELECT sparsevec_norm('{1:3,2:4}/2');
SELECT sparsevec_norm('{2:1}/2');
SELECT sparsevec_norm('{1:3e37,2:4e37}/2')::real;
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2,3:3}/3';
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2}/2';
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2,3:3}/3';
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2}/2';
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2,3:3}/3';
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2}/2';
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2,3:3}/3';
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2}/2';
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2,3:3}/3';
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2}/2';
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2,3:3}/3';
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2}/2';
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2,3:3}/3');
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{}/3');
SELECT sparsevec_cmp('{}/3', '{1:1,2:2,3:3}/3');
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:1,2:2,3:3}/3');
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2}/2');
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:2,2:3,3:4}/3');
SELECT sparsevec_cmp('{1:2,2:3}/2', '{1:1,2:2,3:3}/3');
SELECT round(l2_norm('{1:1,2:1}/2'::sparsevec)::numeric, 5);
SELECT l2_norm('{1:3,2:4}/2'::sparsevec);
SELECT l2_norm('{2:1}/2'::sparsevec);
SELECT l2_norm('{1:3e37,2:4e37}/2'::sparsevec)::real;
SELECT l2_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
SELECT l2_distance('{1:3}/2'::sparsevec, '{2:4}/2');
SELECT l2_distance('{2:4}/2'::sparsevec, '{1:3}/2');
SELECT l2_distance('{}/2'::sparsevec, '{2:1}/2');
SELECT '{}/2'::sparsevec <-> '{1:3,2:4}/2';
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
SELECT sparsevec_negative_inner_product('{1:1,2:2}/2', '{1:2,2:4}/2');
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
SELECT inner_product('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
SELECT inner_product('{1:1,3:3,5:5}/5'::sparsevec, '{2:4,3:6,4:8}/5');
SELECT '{1:1,2:2}/2'::sparsevec <#> '{1:3,2:4}/2';
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{}/2');
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1,2:1}/2');
SELECT cosine_distance('{1:1}/2'::sparsevec, '{2:2}/2');
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1,2:-1}/2');
SELECT cosine_distance('{1:2}/2'::sparsevec, '{2:2}/2');
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1.1,2:1.1}/2');
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1.1,2:-1.1}/2');
SELECT cosine_distance('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
SELECT cosine_distance('{}/1'::sparsevec, '{}/1');
SELECT cosine_distance('{1:2}/2'::sparsevec, '{1:1}/3');
SELECT '{1:1,2:2}/2'::sparsevec <=> '{1:2,2:4}/2';
SELECT l1_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
SELECT l1_distance('{}/2'::sparsevec, '{2:1}/2');
SELECT l1_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
SELECT l1_distance('{1:3e38}/1'::sparsevec, '{1:-3e38}/1');
SELECT l1_distance('{1:1,3:3,5:5,7:7}/8'::sparsevec, '{2:2,4:4,6:6,8:8}/8');
SELECT l1_distance('{1:1,3:3,5:5,7:7,9:9}/9'::sparsevec, '{2:2,4:4,6:6,8:8}/9');
SELECT l2_normalize('{1:3,2:4}/2'::sparsevec);
SELECT l2_normalize('{1:3}/2'::sparsevec);
SELECT l2_normalize('{2:0.1}/2'::sparsevec);
SELECT l2_normalize('{}/2'::sparsevec);
SELECT l2_normalize('{1:3e38}/1'::sparsevec);
SELECT l2_normalize('{1:3e38,2:1e-37}/2'::sparsevec);
SELECT l2_normalize('{2:3e37,4:3e-37,6:4e37,8:4e-37}/9'::sparsevec);

View File

@@ -34,6 +34,8 @@ SELECT '{1:1a}/1'::sparsevec;
SELECT '{1:1,}/1'::sparsevec;
SELECT '{1:0,2:1,3:0}/3'::sparsevec;
SELECT '{2:1,1:1}/2'::sparsevec;
SELECT '{1:1,1:1}/2'::sparsevec;
SELECT '{1:1,2:1,1:1}/2'::sparsevec;
SELECT '{}/5'::sparsevec;
SELECT '{}/-1'::sparsevec;
SELECT '{}/100001'::sparsevec;

View File

@@ -1,10 +1,18 @@
SELECT '[1,2,3]'::vector + '[4,5,6]';
SELECT '[3e38]'::vector + '[3e38]';
SELECT '[1,2]'::vector + '[3]';
SELECT '[1,2,3]'::vector - '[4,5,6]';
SELECT '[-3e38]'::vector - '[3e38]';
SELECT '[1,2]'::vector - '[3]';
SELECT '[1,2,3]'::vector * '[4,5,6]';
SELECT '[1e37]'::vector * '[1e37]';
SELECT '[1e-37]'::vector * '[1e-37]';
SELECT '[1,2]'::vector * '[3]';
SELECT '[1,2,3]'::vector || '[4,5]';
SELECT array_fill(0, ARRAY[16000])::vector || '[1]';
SELECT '[1,2,3]'::vector < '[1,2,3]';
SELECT '[1,2,3]'::vector < '[1,2]';
@@ -27,7 +35,7 @@ SELECT vector_cmp('[1,2,3]', '[1,2]');
SELECT vector_cmp('[1,2]', '[2,3,4]');
SELECT vector_cmp('[2,3]', '[1,2,3]');
SELECT vector_dims('[1,2,3]');
SELECT vector_dims('[1,2,3]'::vector);
SELECT round(vector_norm('[1,1]')::numeric, 5);
SELECT vector_norm('[3,4]');
@@ -38,10 +46,14 @@ SELECT l2_distance('[0,0]'::vector, '[3,4]');
SELECT l2_distance('[0,0]'::vector, '[0,1]');
SELECT l2_distance('[1,2]'::vector, '[3]');
SELECT l2_distance('[3e38]'::vector, '[-3e38]');
SELECT l2_distance('[1,1,1,1,1,1,1,1,1]'::vector, '[1,1,1,1,1,1,1,4,5]');
SELECT '[0,0]'::vector <-> '[3,4]';
SELECT inner_product('[1,2]'::vector, '[3,4]');
SELECT inner_product('[1,2]'::vector, '[3]');
SELECT inner_product('[3e38]'::vector, '[3e38]');
SELECT inner_product('[1,1,1,1,1,1,1,1,1]'::vector, '[1,2,3,4,5,6,7,8,9]');
SELECT '[1,2]'::vector <#> '[3,4]';
SELECT cosine_distance('[1,2]'::vector, '[2,4]');
SELECT cosine_distance('[1,2]'::vector, '[0,0]');
@@ -52,14 +64,26 @@ SELECT cosine_distance('[1,2]'::vector, '[3]');
SELECT cosine_distance('[1,1]'::vector, '[1.1,1.1]');
SELECT cosine_distance('[1,1]'::vector, '[-1.1,-1.1]');
SELECT cosine_distance('[3e38]'::vector, '[3e38]');
SELECT cosine_distance('[1,2,3,4,5,6,7,8,9]'::vector, '[1,2,3,4,5,6,7,8,9]');
SELECT cosine_distance('[1,2,3,4,5,6,7,8,9]'::vector, '[-1,-2,-3,-4,-5,-6,-7,-8,-9]');
SELECT '[1,2]'::vector <=> '[2,4]';
SELECT l1_distance('[0,0]'::vector, '[3,4]');
SELECT l1_distance('[0,0]'::vector, '[0,1]');
SELECT l1_distance('[1,2]'::vector, '[3]');
SELECT l1_distance('[3e38]'::vector, '[-3e38]');
SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::vector, '[1,2,3,4,5,6,7,8,9]');
SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::vector, '[0,3,2,5,4,7,6,9,8]');
SELECT '[0,0]'::vector <+> '[3,4]';
SELECT quantize_binary('[1,0,-1]'::vector);
SELECT quantize_binary('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::vector);
SELECT l2_normalize('[3,4]'::vector);
SELECT l2_normalize('[3,0]'::vector);
SELECT l2_normalize('[0,0.1]'::vector);
SELECT l2_normalize('[0,0]'::vector);
SELECT l2_normalize('[3e38]'::vector);
SELECT binary_quantize('[1,0,-1]'::vector);
SELECT binary_quantize('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::vector);
SELECT subvector('[1,2,3,4,5]'::vector, 1, 3);
SELECT subvector('[1,2,3,4,5]'::vector, 3, 2);

View File

@@ -67,8 +67,8 @@ for (1 .. 20)
}
# Check each index type
my @operators = ("<->", "<#>", "<=>");
my @opclasses = ("vector_l2_ops", "vector_ip_ops", "vector_cosine_ops");
my @operators = ("<->", "<#>", "<=>", "<+>");
my @opclasses = ("vector_l2_ops", "vector_ip_ops", "vector_cosine_ops", "vector_l1_ops");
for my $i (0 .. $#operators)
{

View File

@@ -64,8 +64,8 @@ for (1 .. 20)
}
# Check each index type
my @operators = ("<->", "<#>", "<=>");
my @opclasses = ("vector_l2_ops", "vector_ip_ops", "vector_cosine_ops");
my @operators = ("<->", "<#>", "<=>", "<+>");
my @opclasses = ("vector_l2_ops", "vector_ip_ops", "vector_cosine_ops", "vector_l1_ops");
for my $i (0 .. $#operators)
{

View File

@@ -71,8 +71,8 @@ for (1 .. 20)
}
# Check each index type
my @operators = ("<->", "<#>", "<=>");
my @opclasses = ("halfvec_l2_ops", "halfvec_ip_ops", "halfvec_cosine_ops");
my @operators = ("<->", "<#>", "<=>", "<+>");
my @opclasses = ("halfvec_l2_ops", "halfvec_ip_ops", "halfvec_cosine_ops", "halfvec_l1_ops");
for my $i (0 .. $#operators)
{

View File

@@ -67,8 +67,8 @@ for (1 .. 20)
}
# Check each index type
my @operators = ("<->", "<#>", "<=>");
my @opclasses = ("sparsevec_l2_ops", "sparsevec_ip_ops", "sparsevec_cosine_ops");
my @operators = ("<->", "<#>", "<=>", "<+>");
my @opclasses = ("sparsevec_l2_ops", "sparsevec_ip_ops", "sparsevec_cosine_ops", "sparsevec_l1_ops");
for my $i (0 .. $#operators)
{

View File

@@ -68,8 +68,8 @@ for (1 .. 20)
}
# Check each index type
my @operators = ("<->", "<#>", "<=>");
my @opclasses = ("halfvec_l2_ops", "halfvec_ip_ops", "halfvec_cosine_ops");
my @operators = ("<->", "<#>", "<=>", "<+>");
my @opclasses = ("halfvec_l2_ops", "halfvec_ip_ops", "halfvec_cosine_ops", "halfvec_l1_ops");
for my $i (0 .. $#operators)
{

View File

@@ -64,8 +64,8 @@ for (1 .. 20)
}
# Check each index type
my @operators = ("<->", "<#>", "<=>");
my @opclasses = ("sparsevec_l2_ops", "sparsevec_ip_ops", "sparsevec_cosine_ops");
my @operators = ("<->", "<#>", "<=>", "<+>");
my @opclasses = ("sparsevec_l2_ops", "sparsevec_ip_ops", "sparsevec_cosine_ops", "sparsevec_l1_ops");
for my $i (0 .. $#operators)
{
@@ -99,7 +99,7 @@ for my $i (0 .. $#operators)
}
# Test approximate results
my $min = $operator eq "<#>" ? 0.80 : 0.99;
my $min = $operator eq "<#>" ? 0.79 : 0.99;
test_recall($min, $operator);
$node->safe_psql("postgres", "DROP INDEX idx;");

View File

@@ -105,8 +105,8 @@ for my $i (0 .. $#operators)
if ($operator ne "<#>")
{
# TODO Fix test (uniform random vectors all have similar inner product)
test_recall(1, 0.35, $operator);
test_recall(10, 0.95, $operator);
test_recall(1, 0.34, $operator);
test_recall(10, 0.93, $operator);
}
# Test probes equals lists
@@ -134,8 +134,8 @@ for my $i (0 .. $#operators)
if ($operator ne "<#>")
{
# TODO Fix test (uniform random vectors all have similar inner product)
test_recall(1, 0.35, $operator);
test_recall(10, 0.95, $operator);
test_recall(1, 0.34, $operator);
test_recall(10, 0.93, $operator);
}
# Test probes equals lists

48
test/t/033_comparison.pl Normal file
View File

@@ -0,0 +1,48 @@
use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More;
my $node;
my $array_sql = join(",", ('floor(random() * 2)::int - 1') x 3);
# Initialize node
$node = get_new_node('node');
$node->init;
$node->start;
# Create table
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
$node->safe_psql("postgres", "CREATE TABLE tst (v real[]);");
$node->safe_psql("postgres",
"INSERT INTO tst SELECT ARRAY[$array_sql] FROM generate_series(1, 10000) i;"
);
for (1 .. 50)
{
# Generate query
my @r = ();
for (1 .. (int(rand() * 3) + 2))
{
push(@r, int(rand() * 2) - 1);
}
my $query = "{" . join(",", @r) . "}";
# Get expected result
my $expected = $node->safe_psql("postgres", "SELECT btarraycmp(v, '$query') FROM tst");
# Test vector
my $actual = $node->safe_psql("postgres", "SELECT vector_cmp(v::vector, '$query'::real[]::vector) FROM tst");
is($expected, $actual);
# Test halfvec
$actual = $node->safe_psql("postgres", "SELECT halfvec_cmp(v::halfvec, '$query'::real[]::halfvec) FROM tst");
is($expected, $actual);
# Test sparsevec
$actual = $node->safe_psql("postgres", "SELECT sparsevec_cmp(v::vector::sparsevec, '$query'::real[]::vector::sparsevec) FROM tst");
is($expected, $actual);
}
done_testing();

View File

@@ -0,0 +1,54 @@
use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More;
my $node;
my @queries = ();
my $dim = 5;
my $array_sql = join(",", ('floor(random() * 4)::int - 2') x $dim);
# Initialize node
$node = get_new_node('node');
$node->init;
$node->start;
# Create table
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
$node->safe_psql("postgres", "CREATE TABLE tst (v vector($dim));");
$node->safe_psql("postgres",
"INSERT INTO tst SELECT ARRAY[$array_sql] FROM generate_series(1, 10000) i;"
);
# Generate queries
for (1 .. 20)
{
my @r = ();
for (1 .. $dim)
{
push(@r, int(rand() * 4) - 2);
}
push(@queries, "[" . join(",", @r) . "]");
}
# Check each distance function
my @functions = ("l2_distance", "inner_product", "cosine_distance", "l1_distance");
for my $function (@functions)
{
for my $query (@queries)
{
my $expected = $node->safe_psql("postgres", "SELECT $function(v, '$query') FROM tst");
# Test halfvec
my $actual = $node->safe_psql("postgres", "SELECT $function(v::halfvec, '$query'::vector::halfvec) FROM tst");
is($expected, $actual, "halfvec $function");
# Test sparsevec
$actual = $node->safe_psql("postgres", "SELECT $function(v::sparsevec, '$query'::vector::sparsevec) FROM tst");
is($expected, $actual, "sparsevec $function");
}
}
done_testing();

View File

@@ -0,0 +1,128 @@
use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More;
my $node;
my @queries = ();
my @expected;
my $limit = 20;
my $dim = 52;
my $max = 2**$dim;
sub test_recall
{
my ($probes, $min, $operator) = @_;
my $correct = 0;
my $total = 0;
my $explain = $node->safe_psql("postgres", qq(
SET enable_seqscan = off;
SET ivfflat.probes = $probes;
EXPLAIN ANALYZE SELECT i FROM tst ORDER BY v $operator $queries[0] LIMIT $limit;
));
like($explain, qr/Index Scan using idx on tst/);
for my $i (0 .. $#queries)
{
my $actual = $node->safe_psql("postgres", qq(
SET enable_seqscan = off;
SET ivfflat.probes = $probes;
SELECT i FROM tst ORDER BY v $operator $queries[$i] LIMIT $limit;
));
my @actual_ids = split("\n", $actual);
my @expected_ids = split("\n", $expected[$i]);
my %expected_set = map { $_ => 1 } @expected_ids;
foreach (@actual_ids)
{
if (exists($expected_set{$_}))
{
$correct++;
}
}
$total += $limit;
}
cmp_ok($correct / $total, ">=", $min, $operator);
}
# Initialize node
$node = get_new_node('node');
$node->init;
$node->start;
# Create table
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v bit($dim));");
$node->safe_psql("postgres",
"INSERT INTO tst SELECT i, (random() * $max)::bigint::bit($dim) FROM generate_series(1, 100000) i;"
);
# Generate queries
for (1 .. 20)
{
my $r = int(rand() * $max);
push(@queries, "${r}::bigint::bit($dim)");
}
# Check each index type
my @operators = ("<~>");
my @opclasses = ("bit_hamming_ops");
for my $i (0 .. $#operators)
{
my $operator = $operators[$i];
my $opclass = $opclasses[$i];
# Get exact results
@expected = ();
foreach (@queries)
{
my $res = $node->safe_psql("postgres", qq(
WITH top AS (
SELECT v $operator $_ AS distance FROM tst ORDER BY distance LIMIT $limit
)
SELECT i FROM tst WHERE (v $operator $_) <= (SELECT MAX(distance) FROM top)
));
push(@expected, $res);
}
# Build index serially
$node->safe_psql("postgres", qq(
SET max_parallel_maintenance_workers = 0;
CREATE INDEX idx ON tst USING ivfflat (v $opclass);
));
# Test approximate results
test_recall(1, 0.08, $operator);
test_recall(10, 0.50, $operator);
# Test probes equals lists
test_recall(100, 1.00, $operator);
$node->safe_psql("postgres", "DROP INDEX idx;");
# Build index in parallel
my ($ret, $stdout, $stderr) = $node->psql("postgres", qq(
SET client_min_messages = DEBUG;
SET min_parallel_table_scan_size = 1;
CREATE INDEX idx ON tst USING ivfflat (v $opclass);
));
is($ret, 0, $stderr);
like($stderr, qr/using \d+ parallel workers/);
# Test approximate results
test_recall(1, 0.08, $operator);
test_recall(10, 0.50, $operator);
# Test probes equals lists
test_recall(100, 1.00, $operator);
$node->safe_psql("postgres", "DROP INDEX idx;");
}
done_testing();