Compare commits

..

76 Commits

Author SHA1 Message Date
Andrew Kane
e9d5a0b1aa Improved check [skip ci] 2024-04-17 18:19:03 -07:00
Andrew Kane
52a81b3b9d Improved check [skip ci] 2024-04-17 18:16:58 -07:00
Andrew Kane
c5372e493b Improved check [skip ci] 2024-04-17 18:13:58 -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
45 changed files with 2701 additions and 282 deletions

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ 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\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
HEADERS = src\halfvec.h src\sparsevec.h src\vector.h 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) REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
# For /arch flags # For /arch flags

View File

@@ -138,6 +138,12 @@ Get the nearest neighbors to a vector
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5; SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
``` ```
Supported distance functions are:
- `<->` - L2 distance
- `<#>` - (negative) inner product
- `<=>` - cosine distance
Get the nearest neighbors to a row Get the nearest neighbors to a row
```sql ```sql
@@ -347,6 +353,7 @@ Supported types are:
- `vector` - up to 2,000 dimensions - `vector` - up to 2,000 dimensions
- `halfvec` - up to 4,000 dimensions (unreleased) - `halfvec` - up to 4,000 dimensions (unreleased)
- `bit` - up to 64,000 dimensions (unreleased)
### Query Options ### Query Options
@@ -476,20 +483,20 @@ Also supports Jaccard distance (`<%>`)
Use expression indexing for binary quantization Use expression indexing for binary quantization
```sql ```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 Get the nearest neighbors by Hamming distance
```sql ```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 Re-rank by the original vectors for better recall
```sql ```sql
SELECT * FROM ( 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; ) ORDER BY embedding <=> '[1,-2,3]' LIMIT 5;
``` ```
@@ -509,7 +516,7 @@ Insert vectors
INSERT INTO items (embedding) VALUES ('{1:1,3:2,5:3}/5'), ('{1:4,3:5,5:6}/5'); 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 Get the nearest neighbors by L2 distance
@@ -844,6 +851,7 @@ Operator | Description | Added
\+ | element-wise addition | \+ | element-wise addition |
\- | element-wise subtraction | \- | element-wise subtraction |
\* | element-wise multiplication | 0.5.0 \* | element-wise multiplication | 0.5.0
\|\| | concatenate | unreleased
<-> | Euclidean distance | <-> | Euclidean distance |
<#> | negative inner product | <#> | negative inner product |
<=> | cosine distance | <=> | cosine distance |
@@ -852,11 +860,12 @@ Operator | Description | Added
Function | Description | Added Function | Description | Added
--- | --- | --- --- | --- | ---
binary_quantize(vector) → bit | binary quantize | unreleased
cosine_distance(vector, vector) → double precision | cosine distance | cosine_distance(vector, vector) → double precision | cosine distance |
inner_product(vector, vector) → double precision | inner product | 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 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 subvector(vector, integer, integer) → vector | subvector | unreleased
vector_dims(vector) → integer | number of dimensions | vector_dims(vector) → integer | number of dimensions |
vector_norm(vector) → double precision | Euclidean norm | vector_norm(vector) → double precision | Euclidean norm |
@@ -876,6 +885,10 @@ Each half vector takes `2 * dimensions + 8` bytes of storage. Each element is a
Operator | Description | Added Operator | Description | Added
--- | --- | --- --- | --- | ---
\+ | element-wise addition | unreleased
\- | element-wise subtraction | unreleased
\* | element-wise multiplication | unreleased
\|\| | concatenate | unreleased
<-> | Euclidean distance | unreleased <-> | Euclidean distance | unreleased
<#> | negative inner product | unreleased <#> | negative inner product | unreleased
<=> | cosine distance | unreleased <=> | cosine distance | unreleased
@@ -884,12 +897,22 @@ Operator | Description | Added
Function | Description | Added Function | Description | Added
--- | --- | --- --- | --- | ---
binary_quantize(halfvec) → bit | binary quantize | unreleased
cosine_distance(halfvec, halfvec) → double precision | cosine distance | unreleased cosine_distance(halfvec, halfvec) → double precision | cosine distance | unreleased
inner_product(halfvec, halfvec) → double precision | inner product | 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 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 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 ### Bit Type
@@ -927,8 +950,10 @@ Function | Description | Added
--- | --- | --- --- | --- | ---
cosine_distance(sparsevec, sparsevec) → double precision | cosine distance | unreleased cosine_distance(sparsevec, sparsevec) → double precision | cosine distance | unreleased
inner_product(sparsevec, sparsevec) → double precision | inner product | 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 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 ## Installation Notes - Linux and Mac
@@ -1055,7 +1080,7 @@ Note: Replace `16` with your Postgres server version
Install the FreeBSD package with: Install the FreeBSD package with:
```sh ```sh
pkg install postgresql15-pg_vector pkg install postgresql15-pgvector
``` ```
or the port with: or the port with:

View File

@@ -1,12 +1,22 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION -- 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 \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; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION subvector(vector, int, int) RETURNS vector CREATE FUNCTION subvector(vector, int, int) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 = vector_concat
);
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8 CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -23,6 +33,18 @@ CREATE OPERATOR <%> (
COMMUTATOR = '<%>' 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);
CREATE OPERATOR CLASS bit_jaccard_ops
FOR TYPE bit USING ivfflat AS
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
FUNCTION 1 jaccard_distance(bit, bit),
FUNCTION 3 jaccard_distance(bit, bit);
CREATE OPERATOR CLASS bit_hamming_ops CREATE OPERATOR CLASS bit_hamming_ops
FOR TYPE bit USING hnsw AS FOR TYPE bit USING hnsw AS
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops, OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
@@ -71,15 +93,54 @@ CREATE FUNCTION cosine_distance(halfvec, halfvec) RETURNS float8
CREATE FUNCTION l1_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; AS 'MODULE_PATHNAME', 'halfvec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_norm(halfvec) RETURNS float8 CREATE FUNCTION vector_dims(halfvec) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'halfvec_vector_dims' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION quantize_binary(halfvec) RETURNS bit CREATE FUNCTION l2_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 CREATE FUNCTION subvector(halfvec, int, int) RETURNS halfvec
AS 'MODULE_PATHNAME', 'halfvec_subvector' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 CREATE FUNCTION halfvec_l2_squared_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -89,9 +150,37 @@ CREATE FUNCTION halfvec_negative_inner_product(halfvec, halfvec) RETURNS float8
CREATE FUNCTION halfvec_spherical_distance(halfvec, halfvec) RETURNS float8 CREATE FUNCTION halfvec_spherical_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 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 CREATE FUNCTION halfvec(halfvec, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 CREATE FUNCTION array_to_halfvec(integer[], integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -110,8 +199,14 @@ CREATE FUNCTION halfvec_to_float4(halfvec, integer, boolean) RETURNS real[]
CREATE CAST (halfvec AS halfvec) CREATE CAST (halfvec AS halfvec)
WITH FUNCTION halfvec(halfvec, integer, boolean) AS IMPLICIT; 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[]) 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) CREATE CAST (integer[] AS halfvec)
WITH FUNCTION array_to_halfvec(integer[], integer, boolean) AS ASSIGNMENT; WITH FUNCTION array_to_halfvec(integer[], integer, boolean) AS ASSIGNMENT;
@@ -140,6 +235,69 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>' 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 CREATE OPERATOR CLASS halfvec_l2_ops
FOR TYPE halfvec USING ivfflat AS FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops, OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
@@ -151,15 +309,15 @@ CREATE OPERATOR CLASS halfvec_ip_ops
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops, 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 3 halfvec_spherical_distance(halfvec, halfvec), FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
FUNCTION 4 halfvec_norm(halfvec); FUNCTION 4 l2_norm(halfvec);
CREATE OPERATOR CLASS halfvec_cosine_ops CREATE OPERATOR CLASS halfvec_cosine_ops
FOR TYPE halfvec USING ivfflat AS FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops, 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 2 halfvec_norm(halfvec), FUNCTION 2 l2_norm(halfvec),
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec), FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
FUNCTION 4 halfvec_norm(halfvec); FUNCTION 4 l2_norm(halfvec);
CREATE OPERATOR CLASS halfvec_l2_ops CREATE OPERATOR CLASS halfvec_l2_ops
FOR TYPE halfvec USING hnsw AS FOR TYPE halfvec USING hnsw AS
@@ -175,19 +333,7 @@ CREATE OPERATOR CLASS halfvec_cosine_ops
FOR TYPE halfvec USING hnsw AS FOR TYPE halfvec USING hnsw AS
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops, 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 2 halfvec_norm(halfvec); FUNCTION 2 l2_norm(halfvec);
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 TYPE sparsevec; CREATE TYPE sparsevec;
@@ -224,7 +370,34 @@ CREATE FUNCTION inner_product(sparsevec, sparsevec) RETURNS float8
CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8 CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_l2_squared_distance(sparsevec, sparsevec) RETURNS float8 CREATE FUNCTION sparsevec_l2_squared_distance(sparsevec, sparsevec) RETURNS float8
@@ -246,7 +419,7 @@ CREATE CAST (sparsevec AS sparsevec)
WITH FUNCTION sparsevec(sparsevec, integer, boolean) AS IMPLICIT; WITH FUNCTION sparsevec(sparsevec, integer, boolean) AS IMPLICIT;
CREATE CAST (sparsevec AS vector) 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) CREATE CAST (vector AS sparsevec)
WITH FUNCTION vector_to_sparsevec(vector, integer, boolean) AS IMPLICIT; WITH FUNCTION vector_to_sparsevec(vector, integer, boolean) AS IMPLICIT;
@@ -266,6 +439,51 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>' 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 CREATE OPERATOR CLASS sparsevec_l2_ops
FOR TYPE sparsevec USING hnsw AS FOR TYPE sparsevec USING hnsw AS
OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops, OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops,
@@ -280,4 +498,4 @@ CREATE OPERATOR CLASS sparsevec_cosine_ops
FOR TYPE sparsevec USING hnsw AS FOR TYPE sparsevec USING hnsw AS
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops, 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 2 sparsevec_norm(sparsevec); FUNCTION 2 l2_norm(sparsevec);

View File

@@ -49,6 +49,17 @@ CREATE FUNCTION vector_dims(vector) RETURNS integer
CREATE FUNCTION vector_norm(vector) RETURNS float8 CREATE FUNCTION vector_norm(vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 CREATE FUNCTION vector_add(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 CREATE FUNCTION vector_mul(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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; 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 CREATE FUNCTION vector_lt(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -194,6 +200,10 @@ CREATE OPERATOR * (
COMMUTATOR = * COMMUTATOR = *
); );
CREATE OPERATOR || (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_concat
);
CREATE OPERATOR < ( CREATE OPERATOR < (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_lt, LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_lt,
COMMUTATOR = > , NEGATOR = >= , COMMUTATOR = > , NEGATOR = >= ,
@@ -302,6 +312,8 @@ CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8 CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- bit operators
CREATE OPERATOR <~> ( CREATE OPERATOR <~> (
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance, LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance,
COMMUTATOR = '<~>' COMMUTATOR = '<~>'
@@ -312,6 +324,20 @@ CREATE OPERATOR <%> (
COMMUTATOR = '<%>' 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);
CREATE OPERATOR CLASS bit_jaccard_ops
FOR TYPE bit USING ivfflat AS
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
FUNCTION 1 jaccard_distance(bit, bit),
FUNCTION 3 jaccard_distance(bit, bit);
CREATE OPERATOR CLASS bit_hamming_ops CREATE OPERATOR CLASS bit_hamming_ops
FOR TYPE bit USING hnsw AS FOR TYPE bit USING hnsw AS
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops, OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
@@ -364,17 +390,56 @@ CREATE FUNCTION cosine_distance(halfvec, halfvec) RETURNS float8
CREATE FUNCTION l1_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; AS 'MODULE_PATHNAME', 'halfvec_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION halfvec_norm(halfvec) RETURNS float8 CREATE FUNCTION vector_dims(halfvec) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'halfvec_vector_dims' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION quantize_binary(halfvec) RETURNS bit CREATE FUNCTION l2_norm(halfvec) RETURNS float8
AS 'MODULE_PATHNAME', 'halfvec_quantize_binary' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 CREATE FUNCTION subvector(halfvec, int, int) RETURNS halfvec
AS 'MODULE_PATHNAME', 'halfvec_subvector' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME', 'halfvec_subvector' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- halfvec private functions -- 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 CREATE FUNCTION halfvec_l2_squared_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -384,11 +449,41 @@ CREATE FUNCTION halfvec_negative_inner_product(halfvec, halfvec) RETURNS float8
CREATE FUNCTION halfvec_spherical_distance(halfvec, halfvec) RETURNS float8 CREATE FUNCTION halfvec_spherical_distance(halfvec, halfvec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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;
-- 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 -- halfvec cast functions
CREATE FUNCTION halfvec(halfvec, integer, boolean) RETURNS halfvec CREATE FUNCTION halfvec(halfvec, integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 CREATE FUNCTION array_to_halfvec(integer[], integer, boolean) RETURNS halfvec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -409,8 +504,14 @@ CREATE FUNCTION halfvec_to_float4(halfvec, integer, boolean) RETURNS real[]
CREATE CAST (halfvec AS halfvec) CREATE CAST (halfvec AS halfvec)
WITH FUNCTION halfvec(halfvec, integer, boolean) AS IMPLICIT; 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[]) 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) CREATE CAST (integer[] AS halfvec)
WITH FUNCTION array_to_halfvec(integer[], integer, boolean) AS ASSIGNMENT; WITH FUNCTION array_to_halfvec(integer[], integer, boolean) AS ASSIGNMENT;
@@ -441,8 +542,71 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>' 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 -- 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 CREATE OPERATOR CLASS halfvec_l2_ops
FOR TYPE halfvec USING ivfflat AS FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops, OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
@@ -454,15 +618,15 @@ CREATE OPERATOR CLASS halfvec_ip_ops
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops, 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 3 halfvec_spherical_distance(halfvec, halfvec), FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
FUNCTION 4 halfvec_norm(halfvec); FUNCTION 4 l2_norm(halfvec);
CREATE OPERATOR CLASS halfvec_cosine_ops CREATE OPERATOR CLASS halfvec_cosine_ops
FOR TYPE halfvec USING ivfflat AS FOR TYPE halfvec USING ivfflat AS
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops, 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 2 halfvec_norm(halfvec), FUNCTION 2 l2_norm(halfvec),
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec), FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
FUNCTION 4 halfvec_norm(halfvec); FUNCTION 4 l2_norm(halfvec);
CREATE OPERATOR CLASS halfvec_l2_ops CREATE OPERATOR CLASS halfvec_l2_ops
FOR TYPE halfvec USING hnsw AS FOR TYPE halfvec USING hnsw AS
@@ -478,21 +642,7 @@ CREATE OPERATOR CLASS halfvec_cosine_ops
FOR TYPE halfvec USING hnsw AS FOR TYPE halfvec USING hnsw AS
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops, 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 2 halfvec_norm(halfvec); FUNCTION 2 l2_norm(halfvec);
-- 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;
--- sparsevec type --- sparsevec type
@@ -533,11 +683,38 @@ CREATE FUNCTION inner_product(sparsevec, sparsevec) RETURNS float8
CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8 CREATE FUNCTION cosine_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME', 'sparsevec_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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 -- 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 CREATE FUNCTION sparsevec_l2_squared_distance(sparsevec, sparsevec) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -561,7 +738,7 @@ CREATE CAST (sparsevec AS sparsevec)
WITH FUNCTION sparsevec(sparsevec, integer, boolean) AS IMPLICIT; WITH FUNCTION sparsevec(sparsevec, integer, boolean) AS IMPLICIT;
CREATE CAST (sparsevec AS vector) 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) CREATE CAST (vector AS sparsevec)
WITH FUNCTION vector_to_sparsevec(vector, integer, boolean) AS IMPLICIT; WITH FUNCTION vector_to_sparsevec(vector, integer, boolean) AS IMPLICIT;
@@ -583,8 +760,53 @@ CREATE OPERATOR <=> (
COMMUTATOR = '<=>' 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 -- 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 CREATE OPERATOR CLASS sparsevec_l2_ops
FOR TYPE sparsevec USING hnsw AS FOR TYPE sparsevec USING hnsw AS
OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops, OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops,
@@ -599,4 +821,4 @@ CREATE OPERATOR CLASS sparsevec_cosine_ops
FOR TYPE sparsevec USING hnsw AS FOR TYPE sparsevec USING hnsw AS
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops, 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 2 sparsevec_norm(sparsevec); FUNCTION 2 l2_norm(sparsevec);

View File

@@ -19,8 +19,10 @@
#endif #endif
#endif #endif
float (*HalfvecL2SquaredDistance) (int dim, half * ax, half * bx); float (*HalfvecL2SquaredDistance) (int dim, half * ax, half * bx);
float (*HalfvecInnerProduct) (int dim, half * ax, half * bx); float (*HalfvecInnerProduct) (int dim, half * ax, half * bx);
double (*HalfvecCosineSimilarity) (int dim, half * ax, half * bx);
static float static float
HalfvecL2SquaredDistanceDefault(int dim, half * ax, half * bx) HalfvecL2SquaredDistanceDefault(int dim, half * ax, half * bx)
@@ -117,11 +119,92 @@ HalfvecInnerProductF16cFma(int dim, half * ax, half * bx)
} }
#endif #endif
#ifdef HALFVEC_DISPATCH static double
#define CPU_FEATURE_FMA (1 << 12) HalfvecCosineSimilarityDefault(int dim, half * ax, half * bx)
#define CPU_FEATURE_F16C (1 << 29) {
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_FMA static double
HalfvecCosineSimilarityF16cFma(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
#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) SupportsCpuFeature(unsigned int feature)
{ {
unsigned int exx[4] = {0, 0, 0, 0}; unsigned int exx[4] = {0, 0, 0, 0};
@@ -132,6 +215,12 @@ SupportsCpuFeature(unsigned int feature)
__cpuid(exx, 1); __cpuid(exx, 1);
#endif #endif
if ((exx[2] & CPU_FEATURE_OSXSAVE) != CPU_FEATURE_OSXSAVE)
return false;
if ((_xgetbv(0) & 6) != 6)
return false;
return (exx[2] & feature) == feature; return (exx[2] & feature) == feature;
} }
#endif #endif
@@ -145,12 +234,14 @@ HalfvecInit(void)
*/ */
HalfvecL2SquaredDistance = HalfvecL2SquaredDistanceDefault; HalfvecL2SquaredDistance = HalfvecL2SquaredDistanceDefault;
HalfvecInnerProduct = HalfvecInnerProductDefault; HalfvecInnerProduct = HalfvecInnerProductDefault;
HalfvecCosineSimilarity = HalfvecCosineSimilarityDefault;
#ifdef HALFVEC_DISPATCH #ifdef HALFVEC_DISPATCH
if (SupportsCpuFeature(CPU_FEATURE_FMA | CPU_FEATURE_F16C)) if (SupportsCpuFeature(CPU_FEATURE_AVX | CPU_FEATURE_FMA | CPU_FEATURE_F16C))
{ {
HalfvecL2SquaredDistance = HalfvecL2SquaredDistanceF16cFma; HalfvecL2SquaredDistance = HalfvecL2SquaredDistanceF16cFma;
HalfvecInnerProduct = HalfvecInnerProductF16cFma; HalfvecInnerProduct = HalfvecInnerProductF16cFma;
HalfvecCosineSimilarity = HalfvecCosineSimilarityF16cFma;
} }
#endif #endif
} }

View File

@@ -12,6 +12,7 @@
extern float (*HalfvecL2SquaredDistance) (int dim, half * ax, half * bx); extern float (*HalfvecL2SquaredDistance) (int dim, half * ax, half * bx);
extern float (*HalfvecInnerProduct) (int dim, half * ax, half * bx); extern float (*HalfvecInnerProduct) (int dim, half * ax, half * bx);
extern double (*HalfvecCosineSimilarity) (int dim, half * ax, half * bx);
void HalfvecInit(void); void HalfvecInit(void);
@@ -41,6 +42,19 @@ HalfIsInf(half num)
#endif #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 * Convert a half to a float4
*/ */

View File

@@ -23,6 +23,9 @@
#define TYPALIGN_INT 'i' #define TYPALIGN_INT 'i'
#endif #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 * Get a half from a message buffer
*/ */
@@ -146,6 +149,38 @@ halfvec_isspace(char ch)
return false; 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 * Convert textual representation to internal representation
*/ */
@@ -591,28 +626,11 @@ halfvec_cosine_distance(PG_FUNCTION_ARGS)
{ {
HalfVector *a = PG_GETARG_HALFVEC_P(0); HalfVector *a = PG_GETARG_HALFVEC_P(0);
HalfVector *b = PG_GETARG_HALFVEC_P(1); 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; double similarity;
CheckDims(a, b); CheckDims(a, b);
/* Auto-vectorized */ similarity = HalfvecCosineSimilarity(a->dim, a->x, b->x);
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);
#ifdef _MSC_VER #ifdef _MSC_VER
/* /fp:fast may not propagate NaN */ /* /fp:fast may not propagate NaN */
@@ -677,12 +695,24 @@ halfvec_l1_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8((double) distance); 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 * Get the L2 norm of a half vector
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_norm); PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_norm);
Datum Datum
halfvec_norm(PG_FUNCTION_ARGS) halfvec_l2_norm(PG_FUNCTION_ARGS)
{ {
HalfVector *a = PG_GETARG_HALFVEC_P(0); HalfVector *a = PG_GETARG_HALFVEC_P(0);
half *ax = a->x; half *ax = a->x;
@@ -699,12 +729,195 @@ halfvec_norm(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(norm)); 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 * Quantize a half vector
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_quantize_binary); PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_binary_quantize);
Datum Datum
halfvec_quantize_binary(PG_FUNCTION_ARGS) halfvec_binary_quantize(PG_FUNCTION_ARGS)
{ {
HalfVector *a = PG_GETARG_HALFVEC_P(0); HalfVector *a = PG_GETARG_HALFVEC_P(0);
half *ax = a->x; half *ax = a->x;
@@ -775,3 +988,189 @@ halfvec_cmp_internal(HalfVector * a, HalfVector * b)
return 0; 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);
}

View File

@@ -5,6 +5,7 @@
#include <float.h> #include <float.h>
#include "fmgr.h"
#include "vector.h" #include "vector.h"
#if defined(__x86_64__) || defined(_M_AMD64) #if defined(__x86_64__) || defined(_M_AMD64)
@@ -43,5 +44,6 @@ typedef struct HalfVector
HalfVector *InitHalfVector(int dim); HalfVector *InitHalfVector(int dim);
int halfvec_cmp_internal(HalfVector * a, HalfVector * b); int halfvec_cmp_internal(HalfVector * a, HalfVector * b);
PGDLLEXPORT Datum halfvec_l2_normalize(PG_FUNCTION_ARGS);
#endif #endif

View File

@@ -377,7 +377,8 @@ int HnswGetM(Relation index);
int HnswGetEfConstruction(Relation index); int HnswGetEfConstruction(Relation index);
FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum); FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum);
HnswType HnswGetType(Relation index); HnswType HnswGetType(Relation index);
bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, HnswType type); Datum HnswNormValue(Datum value, HnswType type);
bool HnswCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value);
void HnswCheckValue(Datum value, HnswType type); void HnswCheckValue(Datum value, HnswType type);
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum); Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
void HnswInitPage(Buffer buf, Page page); 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 */ /* Normalize if needed */
if (buildstate->normprocinfo != NULL) if (buildstate->normprocinfo != NULL)
{ {
if (!HnswNormValue(buildstate->normprocinfo, buildstate->collation, &value, buildstate->type)) if (!HnswCheckNorm(buildstate->normprocinfo, buildstate->collation, value))
return false; return false;
value = HnswNormValue(value, buildstate->type);
} }
/* Get datum size */ /* Get datum size */

View File

@@ -626,8 +626,10 @@ HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_ti
normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC); normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
if (normprocinfo != NULL) if (normprocinfo != NULL)
{ {
if (!HnswNormValue(normprocinfo, collation, &value, type)) if (!HnswCheckNorm(normprocinfo, collation, value))
return; return;
value = HnswNormValue(value, type);
} }
HnswInsertTupleOnDisk(index, value, values, isnull, heap_tid, false); HnswInsertTupleOnDisk(index, value, values, isnull, heap_tid, false);

View File

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

View File

@@ -5,6 +5,7 @@
#include "access/generic_xlog.h" #include "access/generic_xlog.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "catalog/pg_type_d.h" #include "catalog/pg_type_d.h"
#include "fmgr.h"
#include "halfutils.h" #include "halfutils.h"
#include "halfvec.h" #include "halfvec.h"
#include "hnsw.h" #include "hnsw.h"
@@ -192,63 +193,29 @@ HnswGetType(Relation index)
} }
/* /*
* Divide by the norm * Normalize value
* */
* Returns false if value should not be indexed Datum
* HnswNormValue(Datum value, HnswType type)
* The caller needs to free the pointer stored in value {
* if it's different than the original value /* TODO Remove type-specific code */
if (type == HNSW_TYPE_VECTOR)
return DirectFunctionCall1(l2_normalize, value);
else if (type == HNSW_TYPE_HALFVEC)
return DirectFunctionCall1(halfvec_l2_normalize, value);
else if (type == HNSW_TYPE_SPARSEVEC)
return DirectFunctionCall1(sparsevec_l2_normalize, value);
else
elog(ERROR, "Unsupported type");
}
/*
* Check if non-zero norm
*/ */
bool bool
HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, HnswType type) HnswCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value)
{ {
double norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value)); return DatumGetFloat8(FunctionCall1Coll(procinfo, collation, value)) > 0;
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;
} }
/* /*

View File

@@ -6,6 +6,7 @@
#include "access/tableam.h" #include "access/tableam.h"
#include "access/parallel.h" #include "access/parallel.h"
#include "access/xact.h" #include "access/xact.h"
#include "bitvector.h"
#include "catalog/index.h" #include "catalog/index.h"
#include "catalog/pg_operator_d.h" #include "catalog/pg_operator_d.h"
#include "catalog/pg_type_d.h" #include "catalog/pg_type_d.h"
@@ -324,6 +325,8 @@ GetMaxDimensions(IvfflatType type)
if (type == IVFFLAT_TYPE_HALFVEC) if (type == IVFFLAT_TYPE_HALFVEC)
maxDimensions *= 2; maxDimensions *= 2;
else if (type == IVFFLAT_TYPE_BIT)
maxDimensions *= 32;
return maxDimensions; return maxDimensions;
} }
@@ -338,6 +341,8 @@ GetItemSize(IvfflatType type, int dimensions)
return VECTOR_SIZE(dimensions); return VECTOR_SIZE(dimensions);
else if (type == IVFFLAT_TYPE_HALFVEC) else if (type == IVFFLAT_TYPE_HALFVEC)
return HALFVEC_SIZE(dimensions); return HALFVEC_SIZE(dimensions);
else if (type == IVFFLAT_TYPE_BIT)
return VARBITTOTALLEN(dimensions);
else else
elog(ERROR, "Unsupported type"); elog(ERROR, "Unsupported type");
} }

View File

@@ -46,7 +46,8 @@
typedef enum IvfflatType typedef enum IvfflatType
{ {
IVFFLAT_TYPE_VECTOR, IVFFLAT_TYPE_VECTOR,
IVFFLAT_TYPE_HALFVEC IVFFLAT_TYPE_HALFVEC,
IVFFLAT_TYPE_BIT
} IvfflatType; } IvfflatType;
/* Build phases */ /* Build phases */
@@ -263,7 +264,7 @@ typedef struct IvfflatScanOpaqueData
typedef IvfflatScanOpaqueData * IvfflatScanOpaque; 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 VECTOR_ARRAY_OFFSET(_arr, _offset) ((char*) (_arr)->items + (_offset) * (_arr)->itemsize)
#define VectorArrayGet(_arr, _offset) VECTOR_ARRAY_OFFSET(_arr, _offset) #define VectorArrayGet(_arr, _offset) VECTOR_ARRAY_OFFSET(_arr, _offset)
#define VectorArraySet(_arr, _offset, _val) memcpy(VECTOR_ARRAY_OFFSET(_arr, _offset), _val, (_arr)->itemsize) #define VectorArraySet(_arr, _offset, _val) memcpy(VECTOR_ARRAY_OFFSET(_arr, _offset), _val, (_arr)->itemsize)

View File

@@ -3,10 +3,12 @@
#include <float.h> #include <float.h>
#include <math.h> #include <math.h>
#include "bitvector.h"
#include "halfutils.h" #include "halfutils.h"
#include "halfvec.h" #include "halfvec.h"
#include "ivfflat.h" #include "ivfflat.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/datum.h" #include "utils/datum.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "vector.h" #include "vector.h"
@@ -134,6 +136,15 @@ CompareHalfVectors(const void *a, const void *b)
return halfvec_cmp_internal((HalfVector *) a, (HalfVector *) 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 * 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); qsort(samples->items, samples->length, samples->itemsize, CompareVectors);
else if (type == IVFFLAT_TYPE_HALFVEC) else if (type == IVFFLAT_TYPE_HALFVEC)
qsort(samples->items, samples->length, samples->itemsize, CompareHalfVectors); qsort(samples->items, samples->length, samples->itemsize, CompareHalfVectors);
else if (type == IVFFLAT_TYPE_BIT)
qsort(samples->items, samples->length, samples->itemsize, CompareBitVectors);
else else
elog(ERROR, "Unsupported type"); elog(ERROR, "Unsupported type");
@@ -191,6 +204,16 @@ QuickCenters(Relation index, VectorArray samples, VectorArray centers, IvfflatTy
for (int j = 0; j < dimensions; j++) for (int j = 0; j < dimensions; j++)
vec->x[j] = Float4ToHalfUnchecked((float) RandomDouble()); 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 else
elog(ERROR, "Unsupported type"); elog(ERROR, "Unsupported type");
@@ -263,6 +286,17 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
aggCenter->x[k] += HalfToFloat4(vec->x[k]); aggCenter->x[k] += HalfToFloat4(vec->x[k]);
} }
} }
else if (type == IVFFLAT_TYPE_BIT)
{
for (int j = 0; j < numSamples; j++)
{
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, closestCenters[j]);
VarBit *vec = (VarBit *) VectorArrayGet(samples, j);
for (int k = 0; k < dimensions; k++)
aggCenter->x[k] += (float) (((VARBITS(vec)[k / 8]) >> (7 - (k % 8))) & 0x01);
}
}
else else
elog(ERROR, "Unsupported type"); elog(ERROR, "Unsupported type");
@@ -308,6 +342,21 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
newCenter->x[k] = Float4ToHalfUnchecked(aggCenter->x[k]); newCenter->x[k] = Float4ToHalfUnchecked(aggCenter->x[k]);
} }
} }
else if (type == IVFFLAT_TYPE_BIT)
{
for (int j = 0; j < numCenters; j++)
{
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, j);
VarBit *newCenter = (VarBit *) VectorArrayGet(newCenters, j);
unsigned char *nx = VARBITS(newCenter);
for (uint32 k = 0; k < VARBITBYTES(newCenter); k++)
nx[k] = 0;
for (int k = 0; k < dimensions; k++)
nx[k / 8] |= (aggCenter->x[k] > 0.5) << (7 - (k % 8));
}
}
/* Normalize if needed */ /* Normalize if needed */
if (normprocinfo != NULL) if (normprocinfo != NULL)
@@ -425,6 +474,18 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
vec->dim = dimensions; vec->dim = dimensions;
} }
} }
else if (type == IVFFLAT_TYPE_BIT)
{
newCenters = VectorArrayInit(numCenters, dimensions, centers->itemsize);
for (int j = 0; j < numCenters; j++)
{
VarBit *vec = (VarBit *) VectorArrayGet(newCenters, j);
SET_VARSIZE(vec, VARBITTOTALLEN(dimensions));
VARBITLEN(vec) = dimensions;
}
}
else else
elog(ERROR, "Unsupported type"); elog(ERROR, "Unsupported type");
@@ -642,23 +703,26 @@ CheckCenters(Relation index, VectorArray centers, IvfflatType type)
elog(ERROR, "Infinite value detected. Please report a bug."); elog(ERROR, "Infinite value detected. Please report a bug.");
} }
} }
else else if (type != IVFFLAT_TYPE_BIT)
elog(ERROR, "Unsupported type"); elog(ERROR, "Unsupported type");
} }
/* Ensure no duplicate centers */ if (type != IVFFLAT_TYPE_BIT)
/* 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)) /* Ensure no duplicate centers */
elog(ERROR, "Duplicate centers detected. Please report a bug."); /* 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 */ /* Ensure no zero vectors for cosine distance */

View File

@@ -3,6 +3,7 @@
#include <float.h> #include <float.h>
#include "access/relscan.h" #include "access/relscan.h"
#include "bitvector.h"
#include "catalog/pg_operator_d.h" #include "catalog/pg_operator_d.h"
#include "catalog/pg_type_d.h" #include "catalog/pg_type_d.h"
#include "halfvec.h" #include "halfvec.h"
@@ -195,6 +196,8 @@ GetScanValue(IndexScanDesc scan)
value = PointerGetDatum(InitVector(so->dimensions)); value = PointerGetDatum(InitVector(so->dimensions));
else if (type == IVFFLAT_TYPE_HALFVEC) else if (type == IVFFLAT_TYPE_HALFVEC)
value = PointerGetDatum(InitHalfVector(so->dimensions)); value = PointerGetDatum(InitHalfVector(so->dimensions));
else if (type == IVFFLAT_TYPE_BIT)
value = PointerGetDatum(InitBitVector(so->dimensions));
else else
elog(ERROR, "Unsupported type"); elog(ERROR, "Unsupported type");
} }

View File

@@ -2,6 +2,7 @@
#include "access/generic_xlog.h" #include "access/generic_xlog.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "fmgr.h"
#include "halfutils.h" #include "halfutils.h"
#include "halfvec.h" #include "halfvec.h"
#include "ivfflat.h" #include "ivfflat.h"
@@ -17,6 +18,9 @@ VectorArrayInit(int maxlen, int dimensions, Size itemsize)
{ {
VectorArray res = palloc(sizeof(VectorArrayData)); VectorArray res = palloc(sizeof(VectorArrayData));
/* Ensure items are aligned to prevent UB */
itemsize = MAXALIGN(itemsize);
res->length = 0; res->length = 0;
res->maxlen = maxlen; res->maxlen = maxlen;
res->dim = dimensions; res->dim = dimensions;
@@ -72,6 +76,9 @@ IvfflatGetType(Relation index)
Form_pg_type type; Form_pg_type type;
IvfflatType result; IvfflatType result;
if (typid == BITOID)
return IVFFLAT_TYPE_BIT;
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tuple)) if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", typid); elog(ERROR, "cache lookup failed for type %u", typid);
@@ -108,25 +115,9 @@ IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, IvfflatType ty
if (norm > 0) if (norm > 0)
{ {
if (type == IVFFLAT_TYPE_VECTOR) if (type == IVFFLAT_TYPE_VECTOR)
{ *value = DirectFunctionCall1(l2_normalize, *value);
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) else if (type == IVFFLAT_TYPE_HALFVEC)
{ *value = DirectFunctionCall1(halfvec_l2_normalize, *value);
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 else
elog(ERROR, "Unsupported type"); elog(ERROR, "Unsupported type");

View File

@@ -579,7 +579,7 @@ vector_to_sparsevec(PG_FUNCTION_ARGS)
if (vec->x[i] != 0) if (vec->x[i] != 0)
{ {
/* Safety check */ /* Safety check */
if (j == nnz) if (j >= result->nnz)
elog(ERROR, "safety check failed"); elog(ERROR, "safety check failed");
result->indices[j] = i + 1; result->indices[j] = i + 1;
@@ -594,12 +594,12 @@ vector_to_sparsevec(PG_FUNCTION_ARGS)
/* /*
* Get the L2 squared distance between sparse vectors * Get the L2 squared distance between sparse vectors
*/ */
static double static float
SparsevecL2SquaredDistance(SparseVector * a, SparseVector * b) SparsevecL2SquaredDistance(SparseVector * a, SparseVector * b)
{ {
float *ax = SPARSEVEC_VALUES(a); float *ax = SPARSEVEC_VALUES(a);
float *bx = SPARSEVEC_VALUES(b); float *bx = SPARSEVEC_VALUES(b);
double distance = 0.0; float distance = 0.0;
int bpos = 0; int bpos = 0;
for (int i = 0; i < a->nnz; i++) for (int i = 0; i < a->nnz; i++)
@@ -673,12 +673,12 @@ sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
/* /*
* Get the inner product of two sparse vectors * Get the inner product of two sparse vectors
*/ */
static double static float
SparsevecInnerProduct(SparseVector * a, SparseVector * b) SparsevecInnerProduct(SparseVector * a, SparseVector * b)
{ {
float *ax = SPARSEVEC_VALUES(a); float *ax = SPARSEVEC_VALUES(a);
float *bx = SPARSEVEC_VALUES(b); float *bx = SPARSEVEC_VALUES(b);
double distance = 0.0; float distance = 0.0;
int bpos = 0; int bpos = 0;
for (int i = 0; i < a->nnz; i++) for (int i = 0; i < a->nnz; i++)
@@ -781,12 +781,61 @@ sparsevec_cosine_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(1.0 - similarity); 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 * Get the L2 norm of a sparse vector
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_norm); PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_norm);
Datum Datum
sparsevec_norm(PG_FUNCTION_ARGS) sparsevec_l2_norm(PG_FUNCTION_ARGS)
{ {
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
float *ax = SPARSEVEC_VALUES(a); float *ax = SPARSEVEC_VALUES(a);
@@ -798,3 +847,205 @@ sparsevec_norm(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(norm)); 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 #ifndef SPARSEVEC_H
#define SPARSEVEC_H #define SPARSEVEC_H
#include "fmgr.h"
#define SPARSEVEC_MAX_DIM 100000 #define SPARSEVEC_MAX_DIM 100000
#define SPARSEVEC_MAX_NNZ 16000 #define SPARSEVEC_MAX_NNZ 16000
@@ -21,5 +23,6 @@ typedef struct SparseVector
} SparseVector; } SparseVector;
SparseVector *InitSparseVector(int dim, int nnz); SparseVector *InitSparseVector(int dim, int nnz);
PGDLLEXPORT Datum sparsevec_l2_normalize(PG_FUNCTION_ARGS);
#endif #endif

View File

@@ -34,7 +34,14 @@
#define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1)) #define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1))
/* target_clones requires glibc */ /* target_clones requires glibc */
#if defined(__x86_64__) && defined(__gnu_linux__) && defined(__has_attribute) && __has_attribute(target_clones) && !defined(__FMA__) #if defined(__gnu_linux__) && defined(__has_attribute)
/* Use separate line for portability */
#if __has_attribute(target_clones)
#define HAVE_TARGET_CLONES
#endif
#endif
#if defined(__x86_64__) && defined(HAVE_TARGET_CLONES) && !defined(__FMA__)
#define VECTOR_DISPATCH __attribute__((target_clones("default", "fma"))) #define VECTOR_DISPATCH __attribute__((target_clones("default", "fma")))
#else #else
#define VECTOR_DISPATCH #define VECTOR_DISPATCH
@@ -653,6 +660,25 @@ vector_negative_inner_product(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8((double) -VectorInnerProduct(a->dim, a->x, b->x)); PG_RETURN_FLOAT8((double) -VectorInnerProduct(a->dim, a->x, b->x));
} }
VECTOR_DISPATCH 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 * Get the cosine distance between two vectors
*/ */
@@ -662,25 +688,11 @@ cosine_distance(PG_FUNCTION_ARGS)
{ {
Vector *a = PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1); 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; double similarity;
CheckDims(a, b); CheckDims(a, b);
/* Auto-vectorized */ similarity = VectorCosineSimilarity(a->dim, a->x, b->x);
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);
#ifdef _MSC_VER #ifdef _MSC_VER
/* /fp:fast may not propagate NaN */ /* /fp:fast may not propagate NaN */
@@ -723,6 +735,18 @@ vector_spherical_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(acos(distance) / M_PI); PG_RETURN_FLOAT8(acos(distance) / M_PI);
} }
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 * Get the L1 distance between two vectors
*/ */
@@ -732,17 +756,10 @@ l1_distance(PG_FUNCTION_ARGS)
{ {
Vector *a = PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
float *ax = a->x;
float *bx = b->x;
float distance = 0.0;
CheckDims(a, b); CheckDims(a, b);
/* Auto-vectorized */ PG_RETURN_FLOAT8((double) VectorL1Distance(a->dim, a->x, b->x));
for (int i = 0; i < a->dim; i++)
distance += fabsf(ax[i] - bx[i]);
PG_RETURN_FLOAT8((double) distance);
} }
/* /*
@@ -775,6 +792,45 @@ vector_norm(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(norm)); 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 * Add vectors
*/ */
@@ -877,12 +933,36 @@ vector_mul(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(result); 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 * Quantize a vector
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(quantize_binary); PGDLLEXPORT PG_FUNCTION_INFO_V1(binary_quantize);
Datum Datum
quantize_binary(PG_FUNCTION_ARGS) binary_quantize(PG_FUNCTION_ARGS)
{ {
Vector *a = PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
float *ax = a->x; float *ax = a->x;
@@ -1107,7 +1187,7 @@ vector_accum(PG_FUNCTION_ARGS)
} }
/* /*
* Combine vectors * Combine vectors or half vectors
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_combine); PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_combine);
Datum Datum

View File

@@ -1,6 +1,8 @@
#ifndef VECTOR_H #ifndef VECTOR_H
#define VECTOR_H #define VECTOR_H
#include "fmgr.h"
#define VECTOR_MAX_DIM 16000 #define VECTOR_MAX_DIM 16000
#define VECTOR_SIZE(_dim) (offsetof(Vector, x) + sizeof(float)*(_dim)) #define VECTOR_SIZE(_dim) (offsetof(Vector, x) + sizeof(float)*(_dim))
@@ -19,5 +21,6 @@ typedef struct Vector
Vector *InitVector(int dim); Vector *InitVector(int dim);
void PrintVector(char *msg, Vector * vector); void PrintVector(char *msg, Vector * vector);
int vector_cmp_internal(Vector * a, Vector * b); int vector_cmp_internal(Vector * a, Vector * b);
PGDLLEXPORT Datum l2_normalize(PG_FUNCTION_ARGS);
#endif #endif

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,2,3]
(1 row) (1 row)
SELECT * FROM t ORDER BY val LIMIT 1; SELECT * FROM t ORDER BY val;
val val
--------- ---------
[0,0,0] [0,0,0]
(1 row) [1,1,1]
[1,2,3]
(4 rows)
DROP TABLE t; DROP TABLE t;

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 round
--------- ---------
1.41421 1.41421
(1 row) (1 row)
SELECT halfvec_norm('[3,4]'); SELECT l2_norm('[3,4]'::halfvec);
halfvec_norm l2_norm
-------------- ---------
5 5
(1 row) (1 row)
SELECT halfvec_norm('[0,1]'); SELECT l2_norm('[0,1]'::halfvec);
halfvec_norm l2_norm
-------------- ---------
1 1
(1 row) (1 row)
SELECT l2_distance('[0,0]'::halfvec, '[3,4]'); SELECT l2_distance('[0,0]'::halfvec, '[3,4]');
@@ -112,6 +272,18 @@ SELECT cosine_distance('[1,1]'::halfvec, '[-1.1,-1.1]');
2 2
(1 row) (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]'; SELECT '[1,2]'::halfvec <=> '[2,4]';
?column? ?column?
---------- ----------
@@ -132,14 +304,44 @@ SELECT l1_distance('[0,0]'::halfvec, '[0,1]');
SELECT l1_distance('[1,2]'::halfvec, '[3]'); SELECT l1_distance('[1,2]'::halfvec, '[3]');
ERROR: different halfvec dimensions 2 and 1 ERROR: different halfvec dimensions 2 and 1
SELECT quantize_binary('[1,0,-1]'::halfvec); SELECT l2_normalize('[3,4]'::halfvec);
quantize_binary 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 100
(1 row) (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); SELECT binary_quantize('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::halfvec);
quantize_binary binary_quantize
----------------- -----------------
01001110101 01001110101
(1 row) (1 row)
@@ -174,3 +376,53 @@ SELECT subvector('[1,2,3,4,5]'::halfvec, 3, -1);
ERROR: halfvec must have at least 1 dimension ERROR: halfvec must have at least 1 dimension
SELECT subvector('[1,2,3,4,5]'::halfvec, -1, 2); SELECT subvector('[1,2,3,4,5]'::halfvec, -1, 2);
ERROR: halfvec must have at least 1 dimension 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,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

@@ -0,0 +1,21 @@
SET enable_seqscan = off;
CREATE TABLE t (val bit(4));
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
CREATE INDEX ON t USING ivfflat (val bit_jaccard_ops) WITH (lists = 1);
INSERT INTO t (val) VALUES (B'1110');
SELECT * FROM t ORDER BY val <%> B'1111';
val
------
1111
1110
1100
0000
(4 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
count
-------
4
(1 row)
DROP TABLE t;

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 round
--------- ---------
1.41421 1.41421
(1 row) (1 row)
SELECT sparsevec_norm('{1:3,2:4}/2'); SELECT l2_norm('{1:3,2:4}/2'::sparsevec);
sparsevec_norm l2_norm
---------------- ---------
5 5
(1 row) (1 row)
SELECT sparsevec_norm('{2:1}/2'); SELECT l2_norm('{2:1}/2'::sparsevec);
sparsevec_norm l2_norm
---------------- ---------
1 1
(1 row) (1 row)
SELECT sparsevec_norm('{1:3e37,2:4e37}/2')::real; SELECT l2_norm('{1:3e37,2:4e37}/2'::sparsevec)::real;
sparsevec_norm l2_norm
---------------- ---------
5e+37 5e+37
(1 row) (1 row)
SELECT l2_distance('{}/2'::sparsevec, '{1:3,2:4}/2'); 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 5
(1 row) (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'); SELECT l2_distance('{}/2'::sparsevec, '{2:1}/2');
l2_distance l2_distance
------------- -------------
@@ -46,10 +172,24 @@ SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
10 10
(1 row) (1 row)
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');
sparsevec_negative_inner_product ERROR: different sparsevec dimensions 2 and 1
---------------------------------- SELECT inner_product('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
-10 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) (1 row)
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2'); 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 NaN
(1 row) (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'); SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1,2:-1}/2');
cosine_distance cosine_distance
----------------- -----------------
@@ -76,11 +228,109 @@ SELECT cosine_distance('{1:2}/2'::sparsevec, '{2:2}/2');
1 1
(1 row) (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'); SELECT cosine_distance('{}/1'::sparsevec, '{}/1');
cosine_distance cosine_distance
----------------- -----------------
NaN NaN
(1 row) (1 row)
SELECT cosine_distance('{1:2}/2'::sparsevec, '{1:1}/3'); SELECT '{1:1,2:2}/2'::sparsevec <=> '{1:2,2:4}/2';
ERROR: different sparsevec dimensions 2 and 3 ?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

@@ -6,6 +6,8 @@ SELECT '[1,2,3]'::vector + '[4,5,6]';
SELECT '[3e38]'::vector + '[3e38]'; SELECT '[3e38]'::vector + '[3e38]';
ERROR: value out of range: overflow 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]'; SELECT '[1,2,3]'::vector - '[4,5,6]';
?column? ?column?
------------ ------------
@@ -14,6 +16,8 @@ SELECT '[1,2,3]'::vector - '[4,5,6]';
SELECT '[-3e38]'::vector - '[3e38]'; SELECT '[-3e38]'::vector - '[3e38]';
ERROR: value out of range: overflow 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]'; SELECT '[1,2,3]'::vector * '[4,5,6]';
?column? ?column?
----------- -----------
@@ -24,6 +28,16 @@ SELECT '[1e37]'::vector * '[1e37]';
ERROR: value out of range: overflow ERROR: value out of range: overflow
SELECT '[1e-37]'::vector * '[1e-37]'; SELECT '[1e-37]'::vector * '[1e-37]';
ERROR: value out of range: underflow 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]'; SELECT '[1,2,3]'::vector < '[1,2,3]';
?column? ?column?
---------- ----------
@@ -138,7 +152,7 @@ SELECT vector_cmp('[2,3]', '[1,2,3]');
1 1
(1 row) (1 row)
SELECT vector_dims('[1,2,3]'); SELECT vector_dims('[1,2,3]'::vector);
vector_dims vector_dims
------------- -------------
3 3
@@ -188,6 +202,12 @@ SELECT l2_distance('[3e38]'::vector, '[-3e38]');
Infinity Infinity
(1 row) (1 row)
SELECT '[0,0]'::vector <-> '[3,4]';
?column?
----------
5
(1 row)
SELECT inner_product('[1,2]'::vector, '[3,4]'); SELECT inner_product('[1,2]'::vector, '[3,4]');
inner_product inner_product
--------------- ---------------
@@ -202,6 +222,12 @@ SELECT inner_product('[3e38]'::vector, '[3e38]');
Infinity Infinity
(1 row) (1 row)
SELECT '[1,2]'::vector <#> '[3,4]';
?column?
----------
-11
(1 row)
SELECT cosine_distance('[1,2]'::vector, '[2,4]'); SELECT cosine_distance('[1,2]'::vector, '[2,4]');
cosine_distance cosine_distance
----------------- -----------------
@@ -252,6 +278,12 @@ SELECT cosine_distance('[3e38]'::vector, '[3e38]');
NaN NaN
(1 row) (1 row)
SELECT '[1,2]'::vector <=> '[2,4]';
?column?
----------
0
(1 row)
SELECT l1_distance('[0,0]'::vector, '[3,4]'); SELECT l1_distance('[0,0]'::vector, '[3,4]');
l1_distance l1_distance
------------- -------------
@@ -272,14 +304,44 @@ SELECT l1_distance('[3e38]'::vector, '[-3e38]');
Infinity Infinity
(1 row) (1 row)
SELECT quantize_binary('[1,0,-1]'::vector); SELECT l2_normalize('[3,4]'::vector);
quantize_binary 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 100
(1 row) (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); SELECT binary_quantize('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::vector);
quantize_binary binary_quantize
----------------- -----------------
01001110101 01001110101
(1 row) (1 row)

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); CREATE INDEX ON t (val);
SELECT * FROM t WHERE val = '[1,2,3]'; 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; DROP TABLE t;

View File

@@ -1,6 +1,45 @@
SELECT round(halfvec_norm('[1,1]')::numeric, 5); SELECT '[1,2,3]'::halfvec + '[4,5,6]';
SELECT halfvec_norm('[3,4]'); SELECT '[65519]'::halfvec + '[65519]';
SELECT halfvec_norm('[0,1]'); 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, '[3,4]');
SELECT l2_distance('[0,0]'::halfvec, '[0,1]'); SELECT l2_distance('[0,0]'::halfvec, '[0,1]');
@@ -22,14 +61,22 @@ SELECT cosine_distance('[1,1]'::halfvec, '[-1,-1]');
SELECT cosine_distance('[1,2]'::halfvec, '[3]'); 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,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 '[1,2]'::halfvec <=> '[2,4]';
SELECT l1_distance('[0,0]'::halfvec, '[3,4]'); SELECT l1_distance('[0,0]'::halfvec, '[3,4]');
SELECT l1_distance('[0,0]'::halfvec, '[0,1]'); SELECT l1_distance('[0,0]'::halfvec, '[0,1]');
SELECT l1_distance('[1,2]'::halfvec, '[3]'); SELECT l1_distance('[1,2]'::halfvec, '[3]');
SELECT quantize_binary('[1,0,-1]'::halfvec); SELECT l2_normalize('[3,4]'::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,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, 1, 3);
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 2); SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 2);
@@ -38,3 +85,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, 1, 0);
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, -1); SELECT subvector('[1,2,3,4,5]'::halfvec, 3, -1);
SELECT subvector('[1,2,3,4,5]'::halfvec, -1, 2); SELECT subvector('[1,2,3,4,5]'::halfvec, -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,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

@@ -0,0 +1,12 @@
SET enable_seqscan = off;
CREATE TABLE t (val bit(4));
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
CREATE INDEX ON t USING ivfflat (val bit_jaccard_ops) WITH (lists = 1);
INSERT INTO t (val) VALUES (B'1110');
SELECT * FROM t ORDER BY val <%> B'1111';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
DROP TABLE t;

View File

@@ -1,18 +1,65 @@
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';
SELECT sparsevec_norm('{1:3,2:4}/2'); SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2}/2';
SELECT sparsevec_norm('{2:1}/2'); SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2,3:3}/3';
SELECT sparsevec_norm('{1:3e37,2:4e37}/2')::real; 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('{}/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 l2_distance('{}/2'::sparsevec, '{2:1}/2');
SELECT '{}/2'::sparsevec <-> '{1:3,2:4}/2'; SELECT '{}/2'::sparsevec <-> '{1:3,2:4}/2';
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:2,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, '{1:2,2:4}/2');
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{}/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:1,2:1}/2'::sparsevec, '{1:-1,2:-1}/2');
SELECT cosine_distance('{1:2}/2'::sparsevec, '{2:2}/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'::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

@@ -1,10 +1,18 @@
SELECT '[1,2,3]'::vector + '[4,5,6]'; SELECT '[1,2,3]'::vector + '[4,5,6]';
SELECT '[3e38]'::vector + '[3e38]'; SELECT '[3e38]'::vector + '[3e38]';
SELECT '[1,2]'::vector + '[3]';
SELECT '[1,2,3]'::vector - '[4,5,6]'; SELECT '[1,2,3]'::vector - '[4,5,6]';
SELECT '[-3e38]'::vector - '[3e38]'; SELECT '[-3e38]'::vector - '[3e38]';
SELECT '[1,2]'::vector - '[3]';
SELECT '[1,2,3]'::vector * '[4,5,6]'; SELECT '[1,2,3]'::vector * '[4,5,6]';
SELECT '[1e37]'::vector * '[1e37]'; SELECT '[1e37]'::vector * '[1e37]';
SELECT '[1e-37]'::vector * '[1e-37]'; 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,3]';
SELECT '[1,2,3]'::vector < '[1,2]'; 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('[1,2]', '[2,3,4]');
SELECT vector_cmp('[2,3]', '[1,2,3]'); 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 round(vector_norm('[1,1]')::numeric, 5);
SELECT vector_norm('[3,4]'); SELECT vector_norm('[3,4]');
@@ -38,10 +46,12 @@ SELECT l2_distance('[0,0]'::vector, '[3,4]');
SELECT l2_distance('[0,0]'::vector, '[0,1]'); SELECT l2_distance('[0,0]'::vector, '[0,1]');
SELECT l2_distance('[1,2]'::vector, '[3]'); SELECT l2_distance('[1,2]'::vector, '[3]');
SELECT l2_distance('[3e38]'::vector, '[-3e38]'); SELECT l2_distance('[3e38]'::vector, '[-3e38]');
SELECT '[0,0]'::vector <-> '[3,4]';
SELECT inner_product('[1,2]'::vector, '[3,4]'); SELECT inner_product('[1,2]'::vector, '[3,4]');
SELECT inner_product('[1,2]'::vector, '[3]'); SELECT inner_product('[1,2]'::vector, '[3]');
SELECT inner_product('[3e38]'::vector, '[3e38]'); SELECT inner_product('[3e38]'::vector, '[3e38]');
SELECT '[1,2]'::vector <#> '[3,4]';
SELECT cosine_distance('[1,2]'::vector, '[2,4]'); SELECT cosine_distance('[1,2]'::vector, '[2,4]');
SELECT cosine_distance('[1,2]'::vector, '[0,0]'); SELECT cosine_distance('[1,2]'::vector, '[0,0]');
@@ -52,14 +62,21 @@ 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('[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('[3e38]'::vector, '[3e38]');
SELECT '[1,2]'::vector <=> '[2,4]';
SELECT l1_distance('[0,0]'::vector, '[3,4]'); SELECT l1_distance('[0,0]'::vector, '[3,4]');
SELECT l1_distance('[0,0]'::vector, '[0,1]'); SELECT l1_distance('[0,0]'::vector, '[0,1]');
SELECT l1_distance('[1,2]'::vector, '[3]'); SELECT l1_distance('[1,2]'::vector, '[3]');
SELECT l1_distance('[3e38]'::vector, '[-3e38]'); SELECT l1_distance('[3e38]'::vector, '[-3e38]');
SELECT quantize_binary('[1,0,-1]'::vector); SELECT l2_normalize('[3,4]'::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,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, 1, 3);
SELECT subvector('[1,2,3,4,5]'::vector, 3, 2); SELECT subvector('[1,2,3,4,5]'::vector, 3, 2);

View File

@@ -99,7 +99,7 @@ for my $i (0 .. $#operators)
} }
# Test approximate results # Test approximate results
my $min = $operator eq "<#>" ? 0.80 : 0.99; my $min = $operator eq "<#>" ? 0.79 : 0.99;
test_recall($min, $operator); test_recall($min, $operator);
$node->safe_psql("postgres", "DROP INDEX idx;"); $node->safe_psql("postgres", "DROP INDEX idx;");

View File

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