diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 6c60e6d5d..000000000 --- a/.dockerignore +++ /dev/null @@ -1,8 +0,0 @@ -/.git/ -/dist/ -/results/ -/tmp_check/ -/sql/vector--?.?.?.sql -regression.* -*.o -*.so diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e58c3b56d..9f541f131 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,8 @@ jobs: fail-fast: false matrix: include: + - postgres: 19 + os: ubuntu-24.04 - postgres: 18 os: ubuntu-24.04 - postgres: 17 @@ -21,14 +23,14 @@ jobs: - postgres: 13 os: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ankane/setup-postgres@v1 with: postgres-version: ${{ matrix.postgres }} dev-files: true - run: make 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 ${{ matrix.postgres >= 18 && '-Wno-missing-field-initializers' || '' }} - run: | export PG_CONFIG=`which pg_config` sudo --preserve-env=PG_CONFIG make install @@ -39,6 +41,38 @@ jobs: sudo apt-get update sudo apt-get install libipc-run-perl - run: make prove_installcheck + ubuntu_spr: + runs-on: ubuntu-24.04 + if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }} + steps: + - uses: actions/checkout@v4 + - name: Install Intel SDE + run: | + curl -o /tmp/sde.tar.xz https://downloadmirror.intel.com/831748/sde-external-9.44.0-2024-08-22-lin.tar.xz + mkdir /tmp/sde && tar -xvf /tmp/sde.tar.xz -C /tmp/sde/ + sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde + - name: Install Postgres + run: | + sudo apt-get install postgresql-16 + sudo apt-get install postgresql-server-dev-16 + sudo systemctl stop postgresql + pgdir=$(pg_config --bindir) + pgdata=/tmp/postgres_data + mkdir $pgdata + $pgdir/initdb -D $pgdata --username=$USER + sudo chmod 777 /var/run/postgresql/ + sde -spr -mix -- $pgdir/pg_ctl -D $pgdata start + - run: make + env: + PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare + - run: | + export PG_CONFIG=`which pg_config` + sudo --preserve-env=PG_CONFIG make install + - run: sde -spr -mix -- make installcheck + - if: ${{ failure() }} + run: cat regression.diffs + - name: Report AVX512 FP16 FMA instruction count + run: cat sde*.txt | grep -E "FMA.*PH" mac: runs-on: ${{ matrix.os }} if: ${{ !startsWith(github.ref_name, 'windows') }} @@ -46,18 +80,18 @@ jobs: fail-fast: false matrix: include: - - postgres: 17 - os: macos-15 + - postgres: 18 + os: macos-26 - postgres: 14 - os: macos-13 + os: macos-15-intel steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ankane/setup-postgres@v1 with: postgres-version: ${{ matrix.postgres }} - run: make env: - PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter + PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unknown-warning-option ${{ matrix.postgres >= 18 && '-Wno-missing-field-initializers' || '' }} - run: make install - run: make installcheck - if: ${{ failure() }} @@ -70,27 +104,35 @@ jobs: tar xf $TAG.tar.gz mv postgres-$TAG postgres env: - TAG: ${{ matrix.postgres == 17 && 'REL_17_2' || 'REL_14_15' }} + TAG: ${{ matrix.postgres == 18 && 'REL_18_0' || 'REL_14_19' }} - run: make prove_installcheck PROVE_FLAGS="-I ./postgres/src/test/perl -I ./test/perl" env: PERL5LIB: /Users/runner/perl5/lib/perl5 - run: make clean && $(brew --prefix llvm@$LLVM_VERSION)/bin/scan-build --status-bugs make env: - LLVM_VERSION: ${{ matrix.os == 'macos-15' && 18 || 15 }} + LLVM_VERSION: ${{ matrix.os == 'macos-26' && 20 || 18 }} PG_CFLAGS: -DUSE_ASSERT_CHECKING windows: - runs-on: windows-latest + runs-on: ${{ matrix.os }} if: ${{ !startsWith(github.ref_name, 'mac') }} + strategy: + fail-fast: false + matrix: + include: + - postgres: 17 + os: windows-2025 + - postgres: 14 + os: windows-2022 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ankane/setup-postgres@v1 with: - postgres-version: 14 + postgres-version: ${{ matrix.postgres }} - run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && ^ nmake /NOLOGO /F Makefile.win && ^ nmake /NOLOGO /F Makefile.win install && ^ - nmake /NOLOGO /F Makefile.win installcheck && ^ + nmake /NOLOGO /F Makefile.win installcheck ${{ matrix.postgres != 17 && 'PG_REGRESS=$(PGROOT)\bin\pg_regress' || '' }} && ^ nmake /NOLOGO /F Makefile.win clean && ^ nmake /NOLOGO /F Makefile.win uninstall shell: cmd @@ -123,10 +165,10 @@ jobs: if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ankane/setup-postgres-valgrind@v1 with: - postgres-version: 17 + postgres-version: 18 check-ub: yes - run: make OPTFLAGS="" - run: sudo --preserve-env=PG_CONFIG make install diff --git a/CHANGELOG.md b/CHANGELOG.md index 757f998d0..a3515593f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.8.2 (unreleased) + +- Fixed `Index Searches` in `EXPLAIN` output for Postgres 18 + +## 0.8.1 (2025-09-04) + +- Added support for Postgres 18 rc1 +- Improved performance of `binary_quantize` function + ## 0.8.0 (2024-10-30) - Added support for iterative index scans diff --git a/Dockerfile b/Dockerfile index 936440928..7e06759d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,11 @@ +# syntax=docker/dockerfile:1 + ARG PG_MAJOR=17 -FROM postgres:$PG_MAJOR +ARG DEBIAN_CODENAME=bookworm +FROM postgres:$PG_MAJOR-$DEBIAN_CODENAME ARG PG_MAJOR -COPY . /tmp/pgvector +ADD https://github.com/pgvector/pgvector.git#v0.8.1 /tmp/pgvector RUN apt-get update && \ apt-mark hold locales && \ diff --git a/META.json b/META.json index b9a68f62a..343518d1c 100644 --- a/META.json +++ b/META.json @@ -2,7 +2,7 @@ "name": "vector", "abstract": "Open-source vector similarity search for Postgres", "description": "Supports L2 distance, inner product, and cosine distance", - "version": "0.8.0", + "version": "0.8.1", "maintainer": [ "Andrew Kane " ], @@ -20,7 +20,7 @@ "vector": { "file": "sql/vector.sql", "docfile": "README.md", - "version": "0.8.0", + "version": "0.8.1", "abstract": "Open-source vector similarity search for Postgres" } }, diff --git a/Makefile b/Makefile index 7a4b88caf..e89ee1342 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,13 @@ EXTENSION = vector -EXTVERSION = 0.8.0 +EXTVERSION = 0.8.1 MODULE_big = vector DATA = $(wildcard sql/*--*--*.sql) DATA_built = sql/$(EXTENSION)--$(EXTVERSION).sql OBJS = src/bitutils.o src/bitvec.o src/halfutils.o src/halfvec.o src/hnsw.o src/hnswbuild.o src/hnswinsert.o src/hnswscan.o src/hnswutils.o src/hnswvacuum.o src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/ivfvacuum.o src/sparsevec.o src/vector.o +ifneq ($(USE_AVX512), 0) + OBJS += src/halfutils_avx512.o +endif HEADERS = src/halfvec.h src/sparsevec.h src/vector.h TESTS = $(wildcard test/sql/*.sql) @@ -31,6 +34,9 @@ endif # - GCC (needs -ftree-vectorize OR -O3) - https://gcc.gnu.org/projects/tree-ssa/vectorization.html # - Clang (could use pragma instead) - https://llvm.org/docs/Vectorizers.html PG_CFLAGS += $(OPTFLAGS) -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math +ifneq ($(USE_AVX512), 0) + PG_CFLAGS += -DUSE_AVX512 +endif # Debug GCC auto-vectorization # PG_CFLAGS += -fopt-info-vec @@ -76,4 +82,9 @@ docker: .PHONY: docker-release docker-release: - docker buildx build --push --pull --no-cache --platform linux/amd64,linux/arm64 --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) . + docker buildx build --push --pull --no-cache --platform linux/amd64,linux/arm64 --build-arg PG_MAJOR=$(PG_MAJOR) --build-arg DEBIAN_CODENAME=bookworm -t pgvector/pgvector:pg$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR)-bookworm -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR)-bookworm . + +.PHONY: docker-release-trixie + +docker-release-trixie: + docker buildx build --push --pull --no-cache --platform linux/amd64,linux/arm64 --build-arg PG_MAJOR=$(PG_MAJOR) --build-arg DEBIAN_CODENAME=trixie -t pgvector/pgvector:pg$(PG_MAJOR)-trixie -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR)-trixie . diff --git a/Makefile.win b/Makefile.win index 8c62f9d5f..46ab3da64 100644 --- a/Makefile.win +++ b/Makefile.win @@ -1,5 +1,5 @@ EXTENSION = vector -EXTVERSION = 0.8.0 +EXTVERSION = 0.8.1 DATA_built = sql\$(EXTENSION)--$(EXTVERSION).sql OBJS = src\bitutils.obj src\bitvec.obj src\halfutils.obj src\halfvec.obj src\hnsw.obj src\hnswbuild.obj src\hnswinsert.obj src\hnswscan.obj src\hnswutils.obj src\hnswvacuum.obj src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\sparsevec.obj src\vector.obj @@ -31,6 +31,9 @@ LIBDIR = $(PGROOT)\lib PKGLIBDIR = $(PGROOT)\lib SHAREDIR = $(PGROOT)\share +# Use $(PGROOT)\bin\pg_regress for Postgres < 17 +PG_REGRESS = $(LIBDIR)\pgxs\src\test\regress\pg_regress + CFLAGS = /nologo /I"$(INCLUDEDIR_SERVER)\port\win32_msvc" /I"$(INCLUDEDIR_SERVER)\port\win32" /I"$(INCLUDEDIR_SERVER)" /I"$(INCLUDEDIR)" CFLAGS = $(CFLAGS) $(PG_CFLAGS) @@ -58,7 +61,7 @@ install: all for %f in ($(HEADERS)) do copy %f "$(INCLUDEDIR_SERVER)\extension\$(EXTENSION)" installcheck: - "$(BINDIR)\pg_regress" --bindir="$(BINDIR)" $(REGRESS_OPTS) $(REGRESS) + "$(PG_REGRESS)" --bindir="$(BINDIR)" $(REGRESS_OPTS) $(REGRESS) uninstall: del /f "$(PKGLIBDIR)\$(SHLIB)" diff --git a/README.md b/README.md index a1077cd45..0f7ee7a51 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Compile and install the extension (supports Postgres 13+) ```sh cd /tmp -git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git +git clone --branch v0.8.1 https://github.com/pgvector/pgvector.git cd pgvector make make install # may need sudo @@ -33,20 +33,12 @@ You can also install it with [Docker](#docker), [Homebrew](#homebrew), [PGXN](#p ### Windows -Ensure [C++ support in Visual Studio](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170#download-and-install-the-tools) is installed, and run: +Ensure [C++ support in Visual Studio](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170#download-and-install-the-tools) is installed and run `x64 Native Tools Command Prompt for VS [version]` as administrator. Then use `nmake` to build: ```cmd -call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" -``` - -Note: The exact path will vary depending on your Visual Studio version and edition - -Then use `nmake` to build: - -```cmd -set "PGROOT=C:\Program Files\PostgreSQL\16" +set "PGROOT=C:\Program Files\PostgreSQL\18" cd %TEMP% -git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git +git clone --branch v0.8.1 https://github.com/pgvector/pgvector.git cd pgvector nmake /F Makefile.win nmake /F Makefile.win install @@ -318,7 +310,9 @@ You can also speed up index creation by increasing the number of parallel worker SET max_parallel_maintenance_workers = 7; -- plus leader ``` -For a large number of workers, you may also need to increase `max_parallel_workers` (8 by default) +For a large number of workers, you may need to increase `max_parallel_workers` (8 by default) + +The [index options](#index-options) also have a significant impact on build time (use the defaults unless seeing low recall) ### Indexing Progress @@ -475,8 +469,6 @@ CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(cate ## Iterative Index Scans -*Added in 0.8.0* - With approximate indexes, queries with filtering can return less results since filtering is applied *after* the index is scanned. Starting with 0.8.0, you can enable iterative index scans, which will automatically scan more of the index until enough results are found (or it reaches `hnsw.max_scan_tuples` or `ivfflat.max_probes`). Iterative scans can use strict or relaxed ordering. @@ -500,9 +492,11 @@ With relaxed ordering, you can use a [materialized CTE](https://www.postgresql.o ```sql WITH relaxed_results AS MATERIALIZED ( SELECT id, embedding <-> '[1,2,3]' AS distance FROM items WHERE category_id = 123 ORDER BY distance LIMIT 5 -) SELECT * FROM relaxed_results ORDER BY distance; +) SELECT * FROM relaxed_results ORDER BY distance + 0; ``` +Note: `+ 0` is needed for Postgres 17+ + For queries that filter by distance, use a materialized CTE and place the distance filter outside of it for best performance (due to the [current behavior](https://www.postgresql.org/message-id/flat/CAOdR5yGUoMQ6j7M5hNUXrySzaqZVGf_Ne%2B8fwZMRKTFxU1nbJg%40mail.gmail.com) of the Postgres executor) ```sql @@ -754,8 +748,6 @@ SELECT query, calls, ROUND((total_plan_time + total_exec_time) / calls) AS avg_t FROM pg_stat_statements ORDER BY total_plan_time + total_exec_time DESC LIMIT 20; ``` -Note: Replace `total_plan_time + total_exec_time` with `total_time` for Postgres < 13 - Monitor recall by comparing results from approximate search with exact search. ```sql @@ -793,7 +785,7 @@ Go | [pgvector-go](https://github.com/pgvector/pgvector-go) Haskell | [pgvector-haskell](https://github.com/pgvector/pgvector-haskell) Java, Kotlin, Groovy, Scala | [pgvector-java](https://github.com/pgvector/pgvector-java) JavaScript, TypeScript | [pgvector-node](https://github.com/pgvector/pgvector-node) -Julia | [pgvector-julia](https://github.com/pgvector/pgvector-julia) +Julia | [Pgvector.jl](https://github.com/pgvector/Pgvector.jl) Lisp | [pgvector-lisp](https://github.com/pgvector/pgvector-lisp) Lua | [pgvector-lua](https://github.com/pgvector/pgvector-lua) Nim | [pgvector-nim](https://github.com/pgvector/pgvector-nim) @@ -820,11 +812,11 @@ Yes, pgvector uses the write-ahead log (WAL), which allows for replication and p #### What if I want to index vectors with more than 2,000 dimensions? -You can use [half-precision indexing](#half-precision-indexing) to index up to 4,000 dimensions or [binary quantization](#binary-quantization) to index up to 64,000 dimensions. Another option is [dimensionality reduction](https://en.wikipedia.org/wiki/Dimensionality_reduction). +You can use [half-precision vectors](#half-precision-vectors) or [half-precision indexing](#half-precision-indexing) to index up to 4,000 dimensions or [binary quantization](#binary-quantization) to index up to 64,000 dimensions. Other options are [indexing subvectors](#indexing-subvectors) (for models that support it) or [dimensionality reduction](https://en.wikipedia.org/wiki/Dimensionality_reduction). #### Can I store vectors with different dimensions in the same column? -You can use `vector` as the type (instead of `vector(3)`). +You can use `vector` as the type (instead of `vector(n)`). ```sql CREATE TABLE embeddings (model_id bigint, item_id bigint, embedding vector, PRIMARY KEY (model_id, item_id)); @@ -1072,7 +1064,7 @@ l2_normalize(sparsevec) → sparsevec | Normalize with Euclidean norm | 0.7.0 If your machine has multiple Postgres installations, specify the path to [pg_config](https://www.postgresql.org/docs/current/app-pgconfig.html) with: ```sh -export PG_CONFIG=/Library/PostgreSQL/17/bin/pg_config +export PG_CONFIG=/Library/PostgreSQL/18/bin/pg_config ``` Then re-run the installation instructions (run `make clean` before `make` if needed). If `sudo` is needed for `make install`, use: @@ -1083,11 +1075,11 @@ sudo --preserve-env=PG_CONFIG make install A few common paths on Mac are: -- EDB installer - `/Library/PostgreSQL/17/bin/pg_config` -- Homebrew (arm64) - `/opt/homebrew/opt/postgresql@17/bin/pg_config` -- Homebrew (x86-64) - `/usr/local/opt/postgresql@17/bin/pg_config` +- EDB installer - `/Library/PostgreSQL/18/bin/pg_config` +- Homebrew (arm64) - `/opt/homebrew/opt/postgresql@18/bin/pg_config` +- Homebrew (x86-64) - `/usr/local/opt/postgresql@18/bin/pg_config` -Note: Replace `17` with your Postgres server version +Note: Replace `18` with your Postgres server version ### Missing Header @@ -1096,10 +1088,10 @@ If compilation fails with `fatal error: postgres.h: No such file or directory`, For Ubuntu and Debian, use: ```sh -sudo apt install postgresql-server-dev-17 +sudo apt install postgresql-server-dev-18 ``` -Note: Replace `17` with your Postgres server version +Note: Replace `18` with your Postgres server version ### Missing SDK @@ -1129,7 +1121,7 @@ If compilation fails with `Cannot open include file: 'postgres.h': No such file ### Mismatched Architecture -If compilation fails with `error C2196: case value '4' already used`, make sure `vcvars64.bat` was called. Then run `nmake /F Makefile.win clean` and re-run the installation instructions. +If compilation fails with `error C2196: case value '4' already used`, make sure you’re using the `x64 Native Tools Command Prompt`. Then run `nmake /F Makefile.win clean` and re-run the installation instructions. ### Missing Symbol @@ -1146,17 +1138,32 @@ If installation fails with `Access is denied`, re-run the installation instructi Get the [Docker image](https://hub.docker.com/r/pgvector/pgvector) with: ```sh -docker pull pgvector/pgvector:pg17 +docker pull pgvector/pgvector:pg18-trixie ``` -This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (replace `17` with your Postgres server version, and run it the same way). +This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (replace `18` with your Postgres server version, and run it the same way). + +Supported tags are: + +- `pg18-trixie`, `0.8.1-pg18-trixie` +- `pg18-bookworm`, `0.8.1-pg18-bookworm`, `pg18`, `0.8.1-pg18` +- `pg17-trixie`, `0.8.1-pg17-trixie` +- `pg17-bookworm`, `0.8.1-pg17-bookworm`, `pg17`, `0.8.1-pg17` +- `pg16-trixie`, `0.8.1-pg16-trixie` +- `pg16-bookworm`, `0.8.1-pg16-bookworm`, `pg16`, `0.8.1-pg16` +- `pg15-trixie`, `0.8.1-pg15-trixie` +- `pg15-bookworm`, `0.8.1-pg15-bookworm`, `pg15`, `0.8.1-pg15` +- `pg14-trixie`, `0.8.1-pg14-trixie` +- `pg14-bookworm`, `0.8.1-pg14-bookworm`, `pg14`, `0.8.1-pg14` +- `pg13-trixie`, `0.8.1-pg13-trixie` +- `pg13-bookworm`, `0.8.1-pg13-bookworm`, `pg13`, `0.8.1-pg13` You can also build the image manually: ```sh -git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git +git clone --branch v0.8.1 https://github.com/pgvector/pgvector.git cd pgvector -docker build --pull --build-arg PG_MAJOR=17 -t myuser/pgvector . +docker build --pull --build-arg PG_MAJOR=18 -t myuser/pgvector . ``` If you increase `maintenance_work_mem`, make sure `--shm-size` is at least that size to avoid an error with parallel HNSW index builds. @@ -1173,7 +1180,7 @@ With Homebrew Postgres, you can use: brew install pgvector ``` -Note: This only adds it to the `postgresql@17` and `postgresql@14` formulas +Note: This only adds it to the `postgresql@18` and `postgresql@17` formulas ### PGXN @@ -1188,29 +1195,29 @@ pgxn install vector Debian and Ubuntu packages are available from the [PostgreSQL APT Repository](https://wiki.postgresql.org/wiki/Apt). Follow the [setup instructions](https://wiki.postgresql.org/wiki/Apt#Quickstart) and run: ```sh -sudo apt install postgresql-17-pgvector +sudo apt install postgresql-18-pgvector ``` -Note: Replace `17` with your Postgres server version +Note: Replace `18` with your Postgres server version ### Yum RPM packages are available from the [PostgreSQL Yum Repository](https://yum.postgresql.org/). Follow the [setup instructions](https://www.postgresql.org/download/linux/redhat/) for your distribution and run: ```sh -sudo yum install pgvector_17 +sudo yum install pgvector_18 # or -sudo dnf install pgvector_17 +sudo dnf install pgvector_18 ``` -Note: Replace `17` with your Postgres server version +Note: Replace `18` with your Postgres server version ### pkg Install the FreeBSD package with: ```sh -pkg install postgresql16-pgvector +pkg install postgresql17-pgvector ``` or the port with: diff --git a/sql/vector--0.8.0--0.8.1.sql b/sql/vector--0.8.0--0.8.1.sql new file mode 100644 index 000000000..547bd44a3 --- /dev/null +++ b/sql/vector--0.8.0--0.8.1.sql @@ -0,0 +1,2 @@ +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION vector UPDATE TO '0.8.1'" to load this file. \quit diff --git a/src/halfutils.c b/src/halfutils.c index d16909409..bdc370a19 100644 --- a/src/halfutils.c +++ b/src/halfutils.c @@ -5,6 +5,7 @@ #ifdef HALFVEC_DISPATCH #include +#include "halfutils_avx512.h" #if defined(USE__GET_CPUID) #include @@ -24,6 +25,8 @@ float (*HalfvecInnerProduct) (int dim, half * ax, half * bx); double (*HalfvecCosineSimilarity) (int dim, half * ax, half * bx); float (*HalfvecL1Distance) (int dim, half * ax, half * bx); +void (*Float4ToHalfVector) (Vector * vec, HalfVector * result); + static float HalfvecL2SquaredDistanceDefault(int dim, half * ax, half * bx) { @@ -237,6 +240,13 @@ HalfvecL1DistanceF16c(int dim, half * ax, half * bx) } #endif +static void +Float4ToHalfVectorDefault(Vector * vec, HalfVector * result) { + for (int i = 0; i < vec->dim; i++) + result->x[i] = Float4ToHalf(vec->x[i]); +} + + #ifdef HALFVEC_DISPATCH #define CPU_FEATURE_FMA (1 << 12) #define CPU_FEATURE_OSXSAVE (1 << 27) @@ -284,6 +294,7 @@ HalfvecInit(void) HalfvecInnerProduct = HalfvecInnerProductDefault; HalfvecCosineSimilarity = HalfvecCosineSimilarityDefault; HalfvecL1Distance = HalfvecL1DistanceDefault; + Float4ToHalfVector = Float4ToHalfVectorDefault; #ifdef HALFVEC_DISPATCH if (SupportsCpuFeature(CPU_FEATURE_AVX | CPU_FEATURE_F16C | CPU_FEATURE_FMA)) @@ -294,5 +305,16 @@ HalfvecInit(void) /* Does not require FMA, but keep logic simple */ HalfvecL1Distance = HalfvecL1DistanceF16c; } + +#if defined(USE_AVX512) && defined(HAVE_AVX512FP16) + if (SupportsAvx512Fp16()) + { + HalfvecL2SquaredDistance = HalfvecL2SquaredDistanceAvx512; + HalfvecInnerProduct = HalfvecInnerProductAvx512; + HalfvecCosineSimilarity = HalfvecCosineSimilarityAvx512; + HalfvecL1Distance = HalfvecL1DistanceAvx512; + Float4ToHalfVector = Float4ToHalfVectorAvx512; + } +#endif #endif } diff --git a/src/halfutils.h b/src/halfutils.h index c684f72d7..1a33d1748 100644 --- a/src/halfutils.h +++ b/src/halfutils.h @@ -5,6 +5,7 @@ #include "common/shortest_dec.h" #include "halfvec.h" +#include "vector.h" #ifdef F16C_SUPPORT #include @@ -15,6 +16,8 @@ extern float (*HalfvecInnerProduct) (int dim, half * ax, half * bx); extern double (*HalfvecCosineSimilarity) (int dim, half * ax, half * bx); extern float (*HalfvecL1Distance) (int dim, half * ax, half * bx); +extern void (*Float4ToHalfVector) (Vector * vec, HalfVector * result); + void HalfvecInit(void); /* diff --git a/src/halfutils_avx512.c b/src/halfutils_avx512.c new file mode 100644 index 000000000..27d907781 --- /dev/null +++ b/src/halfutils_avx512.c @@ -0,0 +1,449 @@ +#ifdef USE_AVX512 +#include "halfutils_avx512.h" + +#ifdef HAVE_AVX512FP16 +#include "common/shortest_dec.h" + +#include +#include + +#if defined(USE__GET_CPUID) +#include +#else +#include +#endif + +#ifdef _MSC_VER +#define TARGET_AVX512FP16 +#else +#define TARGET_AVX512FP16 __attribute__((target("avx512fp16,avx512f,avx512dq,avx512vl,avx512bw"))) +#endif + +TARGET_AVX512FP16 static inline bool +HasInfinity(__m512h val) { + /* Test for positive and negative infinity */ + __mmask32 mask = _mm512_fpclass_ph_mask(val, 0x08 + 0x10); + return mask != 0; +} + +TARGET_AVX512FP16 static inline __m512 +ConvertToFp32Sum(__m512h val) { + __m256h val_lower = _mm256_castsi256_ph(_mm512_extracti32x8_epi32(_mm512_castph_si512(val), 0)); + __m256h val_upper = _mm256_castsi256_ph(_mm512_extracti32x8_epi32(_mm512_castph_si512(val), 1)); + return _mm512_add_ps(_mm512_cvtxph_ps(val_lower), _mm512_cvtxph_ps(val_upper)); +} + +TARGET_AVX512FP16 float +HalfvecL2SquaredDistanceAvx512(int dim, half * ax, half * bx) +{ + float distance; + int i; + unsigned long mask; + + /* For FP16 computation */ + __m512h axi_512h; + __m512h bxi_512h; + __m512h diff_512h; + __m512h dist_512h = _mm512_setzero_ph(); + __m512h dist_512h_temp; + + /* For FP32 computation */ + __m256h axi_256h; + __m256h bxi_256h; + __m512 axi_512; + __m512 bxi_512; + __m512 diff_512; + __m512 dist_512; + + /* FP16 computation */ + for (i = 0; i < dim; i += 32) + { + if (dim - i < 32) + { + mask = (1 << (dim - i)) - 1; + axi_512h = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, ax + i)); + bxi_512h = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, bx + i)); + } + else + { + axi_512h = _mm512_loadu_ph(ax + i); + bxi_512h = _mm512_loadu_ph(bx + i); + } + diff_512h = _mm512_sub_ph(axi_512h, bxi_512h); + dist_512h_temp = _mm512_fmadd_ph(diff_512h, diff_512h, dist_512h); + + /* if overflow, continue with FP32 */ + if (HasInfinity(dist_512h_temp)) + break; + else + dist_512h = dist_512h_temp; + } + dist_512 = ConvertToFp32Sum(dist_512h); + + /* FP32 computation */ + for (; i < dim; i += 16) + { + if (dim - i < 16) + { + mask = (1 << (dim - i)) - 1; + axi_256h = _mm256_castsi256_ph(_mm256_maskz_loadu_epi16(mask, ax + i)); + bxi_256h = _mm256_castsi256_ph(_mm256_maskz_loadu_epi16(mask, bx + i)); + } + else + { + axi_256h = _mm256_loadu_ph(ax + i); + bxi_256h = _mm256_loadu_ph(bx + i); + } + axi_512 = _mm512_cvtxph_ps(axi_256h); + bxi_512 = _mm512_cvtxph_ps(bxi_256h); + diff_512 = _mm512_sub_ps(axi_512, bxi_512); + dist_512 = _mm512_fmadd_ps(diff_512, diff_512, dist_512); + } + + distance = _mm512_reduce_add_ps(dist_512); + return distance; +} + +TARGET_AVX512FP16 float +HalfvecInnerProductAvx512(int dim, half * ax, half * bx) +{ + float distance; + int i; + unsigned int mask; + + /* For FP16 computation */ + __m512h axi_512h; + __m512h bxi_512h; + __m512h dist_512h = _mm512_setzero_ph(); + __m512h dist_512h_temp; + + /* For FP32 computation */ + __m256h axi_256h; + __m256h bxi_256h; + __m512 axi_512; + __m512 bxi_512; + __m512 dist_512; + + /* FP16 computation */ + for (i = 0; i < dim; i += 32) + { + if (dim - i < 32) + { + mask = (1 << (dim - i)) - 1; + axi_512h = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, ax + i)); + bxi_512h = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, bx + i)); + } + else + { + axi_512h = _mm512_loadu_ph(ax + i); + bxi_512h = _mm512_loadu_ph(bx + i); + } + dist_512h_temp = _mm512_fmadd_ph(axi_512h, bxi_512h, dist_512h); + + /* if overflow, continue with FP32 */ + if (HasInfinity(dist_512h_temp)) + break; + else + dist_512h = dist_512h_temp; + } + dist_512 = ConvertToFp32Sum(dist_512h); + + /* FP32 computation */ + for (; i < dim; i += 16) + { + if (dim - i < 16) + { + mask = (1 << (dim - i)) - 1; + axi_256h = _mm256_castsi256_ph(_mm256_maskz_loadu_epi16(mask, ax + i)); + bxi_256h = _mm256_castsi256_ph(_mm256_maskz_loadu_epi16(mask, bx + i)); + } + else + { + axi_256h = _mm256_loadu_ph(ax + i); + bxi_256h = _mm256_loadu_ph(bx + i); + } + axi_512 = _mm512_cvtxph_ps(axi_256h); + bxi_512 = _mm512_cvtxph_ps(bxi_256h); + dist_512 = _mm512_fmadd_ps(axi_512, bxi_512, dist_512); + } + + distance = _mm512_reduce_add_ps(dist_512); + return distance; +} + +TARGET_AVX512FP16 double +HalfvecCosineSimilarityAvx512(int dim, half * ax, half * bx) +{ + float similarity; + float norma; + float normb; + int i; + unsigned int mask; + + /* For FP16 computation */ + __m512h axi_512h; + __m512h bxi_512h; + __m512h sim_512h = _mm512_setzero_ph(); + __m512h na_512h = _mm512_setzero_ph(); + __m512h nb_512h = _mm512_setzero_ph(); + __m512h sim_512h_temp; + __m512h na_512h_temp; + __m512h nb_512h_temp; + + /* For FP32 computation */ + __m256h axi_256h; + __m256h bxi_256h; + __m512 axi_512; + __m512 bxi_512; + __m512 sim_512; + __m512 na_512; + __m512 nb_512; + + /* FP16 computation */ + for (i = 0; i < dim; i += 32) + { + if (dim - i < 32) { + mask = (1 << (dim - i)) - 1; + axi_512h = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, ax + i)); + bxi_512h = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, bx + i)); + } + else { + axi_512h = _mm512_loadu_ph(ax + i); + bxi_512h = _mm512_loadu_ph(bx + i); + } + sim_512h_temp = _mm512_fmadd_ph(axi_512h, bxi_512h, sim_512h); + na_512h_temp = _mm512_fmadd_ph(axi_512h, axi_512h, na_512h); + nb_512h_temp = _mm512_fmadd_ph(bxi_512h, bxi_512h, nb_512h); + + /* if overflow, continue with FP32 */ + if (HasInfinity(sim_512h_temp) || + HasInfinity(na_512h_temp) || + HasInfinity(nb_512h_temp)) + break; + else + { + sim_512h = sim_512h_temp; + na_512h = na_512h_temp; + nb_512h = nb_512h_temp; + } + } + sim_512 = ConvertToFp32Sum(sim_512h); + na_512 = ConvertToFp32Sum(na_512h); + nb_512 = ConvertToFp32Sum(nb_512h); + + /* FP32 computation */ + for (; i < dim; i += 16) + { + if (dim - i < 16) + { + mask = (1 << (dim - i)) - 1; + axi_256h = _mm256_castsi256_ph(_mm256_maskz_loadu_epi16(mask, ax + i)); + bxi_256h = _mm256_castsi256_ph(_mm256_maskz_loadu_epi16(mask, bx + i)); + } + else + { + axi_256h = _mm256_loadu_ph(ax + i); + bxi_256h = _mm256_loadu_ph(bx + i); + } + axi_512 = _mm512_cvtxph_ps(axi_256h); + bxi_512 = _mm512_cvtxph_ps(bxi_256h); + sim_512 = _mm512_fmadd_ps(axi_512, bxi_512, sim_512); + na_512 = _mm512_fmadd_ps(axi_512, axi_512, na_512); + nb_512 = _mm512_fmadd_ps(bxi_512, bxi_512, nb_512); + } + + similarity = _mm512_reduce_add_ps(sim_512); + norma = _mm512_reduce_add_ps(na_512); + normb = _mm512_reduce_add_ps(nb_512); + + /* Use sqrt(a * b) over sqrt(a) * sqrt(b) */ + return (double) similarity / sqrt((double) norma * (double) normb); +} + +TARGET_AVX512FP16 float +HalfvecL1DistanceAvx512(int dim, half * ax, half * bx) +{ + float distance; + int i; + unsigned long mask; + + /* For FP16 computation */ + __m512h axi_512h; + __m512h bxi_512h; + __m512h dist_512h = _mm512_setzero_ph(); + __m512h dist_512h_temp; + + /* For FP32 computation */ + __m256h axi_256h; + __m256h bxi_256h; + __m512 axi_512; + __m512 bxi_512; + __m512 dist_512; + + /* FP16 computation */ + for (i = 0; i < dim; i += 32) + { + if (dim - i < 32) + { + mask = (1 << (dim - i)) - 1; + axi_512h = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, ax + i)); + bxi_512h = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, bx + i)); + } + else + { + axi_512h = _mm512_loadu_ph(ax + i); + bxi_512h = _mm512_loadu_ph(bx + i); + } + dist_512h_temp = _mm512_add_ph(dist_512h, _mm512_abs_ph(_mm512_sub_ph(axi_512h, bxi_512h))); + + /* if overflow, continue with FP32 */ + if (HasInfinity(dist_512h_temp)) + break; + else + dist_512h = dist_512h_temp; + } + dist_512 = ConvertToFp32Sum(dist_512h); + + /* FP32 computation */ + for (; i < dim; i += 16) + { + if (dim - i < 16) + { + mask = (1 << (dim - i)) - 1; + axi_256h = _mm256_castsi256_ph(_mm256_maskz_loadu_epi16(mask, ax + i)); + bxi_256h = _mm256_castsi256_ph(_mm256_maskz_loadu_epi16(mask, bx + i)); + } + else + { + axi_256h = _mm256_loadu_ph(ax + i); + bxi_256h = _mm256_loadu_ph(bx + i); + } + axi_512 = _mm512_cvtxph_ps(axi_256h); + bxi_512 = _mm512_cvtxph_ps(bxi_256h); + dist_512 = _mm512_add_ps(dist_512, _mm512_abs_ps(_mm512_sub_ps(axi_512, bxi_512))); + } + + distance = _mm512_reduce_add_ps(dist_512); + + return distance; +} + +TARGET_AVX512FP16 void +Float4ToHalfVectorAvx512(Vector * vec, HalfVector * result) +{ + unsigned long mask; + __m512 vec_512; + __m256h vec_256h; + __mmask16 vec_512_inf; + __mmask16 vec_256h_inf; + + for (int i = 0; i < vec->dim; i += 16) + { + if (vec->dim - i < 16) + { + mask = (1 << (vec->dim - i)) - 1; + vec_512 = _mm512_maskz_loadu_ps(mask, vec->x + i); + vec_256h = _mm512_cvtxps_ph(vec_512); + _mm256_mask_storeu_epi16(result->x + i, mask, _mm256_castph_si256(vec_256h)); + } + else + { + vec_512 = _mm512_loadu_ps(vec->x + i); + vec_256h = _mm512_cvtxps_ph(vec_512); + _mm256_storeu_ph(result->x + i, vec_256h); + } + + /* Test for positive and negative infinity */ + vec_512_inf = _mm512_fpclass_ps_mask(vec_512, 0x08 + 0x10); + vec_256h_inf = _mm256_fpclass_ph_mask(vec_256h, 0x08 + 0x10); + if (unlikely(vec_512_inf != vec_256h_inf)) + { + float num; + char* buf; + + __mmask16 diff = _kxor_mask16(vec_512_inf, vec_256h_inf); + /* Find first element in vector to overflow after conversion (first bit set) */ + int count = 0; + while (diff % 2 == 0) { + diff >>= 1; + count++; + } + num = vec->x[i + count]; + + /* TODO Avoid duplicate code in Float4ToHalf */ + buf = palloc(FLOAT_SHORTEST_DECIMAL_LEN); + + float_to_shortest_decimal_buf(num, buf); + + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("\"%s\" is out of range for type halfvec", buf))); + } + } +} + +#define CPU_FEATURE_OSXSAVE (1 << 27) +#define CPU_FEATURE_AVX512F (1 << 16) +#define CPU_FEATURE_AVX512DQ (1 << 17) +#define CPU_FEATURE_AVX512_FP16 (1 << 23) +#define CPU_FEATURE_AVX512BW (1 << 30) +#define CPU_FEATURE_AVX512VL (1 << 31) + +#ifdef _MSC_VER +#define TARGET_XSAVE +#else +#define TARGET_XSAVE __attribute__((target("xsave"))) +#endif + +TARGET_XSAVE static bool +SupportsOsXsave() +{ + unsigned int exx[4] = {0, 0, 0, 0}; + +#if defined(HAVE__GET_CPUID) + __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]); +#else + __cpuid(exx, 1); +#endif + + return (exx[2] & CPU_FEATURE_OSXSAVE) == CPU_FEATURE_OSXSAVE; +} + +TARGET_XSAVE bool +SupportsAvx512Fp16() +{ + unsigned int exx[4] = {0, 0, 0, 0}; + + /* AVX512 features required: + * AVX512F : sub/fma/add instructions + * AVX512DQ: _mm512_extracti32x8_epi32 + * AVX512VL: _mm256_loadu_ph + * AVX512BW: masked loads + */ + unsigned int features = CPU_FEATURE_AVX512F | + CPU_FEATURE_AVX512DQ | + CPU_FEATURE_AVX512VL | + CPU_FEATURE_AVX512BW; + + /* Check OS supports XSAVE */ + if (!SupportsOsXsave()) + return false; + + /* Check XMM, YMM, and ZMM registers are enabled */ + if ((_xgetbv(0) & 0xe6) != 0xe6) + return false; + +#if defined(HAVE__GET_CPUID) + __get_cpuid_count(7, 0, &exx[0], &exx[1], &exx[2], &exx[3]); +#elif defined(HAVE__CPUID) + __cpuid(exx, 7, 0); +#endif + + if ((exx[1] & features) != features) + return false; + + return (exx[3] & CPU_FEATURE_AVX512_FP16) == CPU_FEATURE_AVX512_FP16; +} + +#endif +#endif diff --git a/src/halfutils_avx512.h b/src/halfutils_avx512.h new file mode 100644 index 000000000..9f94f11dc --- /dev/null +++ b/src/halfutils_avx512.h @@ -0,0 +1,25 @@ +#ifndef HALFUTILS_AVX512_H +#define HALFUTILS_AVX512_H + +#ifdef USE_AVX512 +#include "postgres.h" +#include "halfvec.h" +#include "vector.h" + +#if (defined(__GNUC__) && (__GNUC__ >= 12)) || \ + (defined(__clang__) && (__clang_major__ >= 16)) || \ + (defined __AVX512FP16__) +#define HAVE_AVX512FP16 +#endif + +#ifdef HAVE_AVX512FP16 +extern float HalfvecL2SquaredDistanceAvx512(int dim, half * ax, half * bx); +extern float HalfvecInnerProductAvx512(int dim, half * ax, half * bx); +extern double HalfvecCosineSimilarityAvx512(int dim, half * ax, half * bx); +extern float HalfvecL1DistanceAvx512(int dim, half * ax, half * bx); +extern void Float4ToHalfVectorAvx512(Vector * vec, HalfVector * result); + +extern bool SupportsAvx512Fp16(void); +#endif +#endif +#endif diff --git a/src/halfvec.c b/src/halfvec.c index aad320b1c..f9a42a559 100644 --- a/src/halfvec.c +++ b/src/halfvec.c @@ -533,8 +533,7 @@ vector_to_halfvec(PG_FUNCTION_ARGS) result = InitHalfVector(vec->dim); - for (int i = 0; i < vec->dim; i++) - result->x[i] = Float4ToHalf(vec->x[i]); + Float4ToHalfVector(vec, result); PG_RETURN_POINTER(result); } @@ -898,8 +897,21 @@ halfvec_binary_quantize(PG_FUNCTION_ARGS) half *ax = a->x; VarBit *result = InitBitVector(a->dim); unsigned char *rx = VARBITS(result); + int i = 0; + int count = (a->dim / 8) * 8; - for (int i = 0; i < a->dim; i++) + /* Auto-vectorized on aarch64 */ + for (; i < count; i += 8) + { + unsigned char result_byte = 0; + + for (int j = 0; j < 8; j++) + result_byte |= (HalfToFloat4(ax[i + j]) > 0) << (7 - j); + + rx[i / 8] = result_byte; + } + + for (; i < a->dim; i++) rx[i / 8] |= (HalfToFloat4(ax[i]) > 0) << (7 - (i % 8)); PG_RETURN_VARBIT_P(result); diff --git a/src/hnsw.c b/src/hnsw.c index 5bfc6193e..1d56ef6ea 100644 --- a/src/hnsw.c +++ b/src/hnsw.c @@ -52,12 +52,20 @@ HnswInitLockTranche(void) sizeof(int) * 1, &found); if (!found) + { +#if PG_VERSION_NUM >= 190000 + tranche_ids[0] = LWLockNewTrancheId("HnswBuild"); +#else tranche_ids[0] = LWLockNewTrancheId(); +#endif + } hnsw_lock_tranche_id = tranche_ids[0]; LWLockRelease(AddinShmemInitLock); +#if PG_VERSION_NUM < 190000 /* Per-backend registration of the tranche ID */ LWLockRegisterTranche(hnsw_lock_tranche_id, "HnswBuild"); +#endif } /* @@ -130,7 +138,7 @@ hnswcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Relation index; /* Never use index without order */ - if (path->indexorderbys == NULL) + if (path->indexorderbys == NIL) { *indexStartupCost = get_float8_infinity(); *indexTotalCost = get_float8_infinity(); @@ -259,6 +267,11 @@ hnswhandler(PG_FUNCTION_ARGS) amroutine->amoptsprocnum = 0; amroutine->amcanorder = false; amroutine->amcanorderbyop = true; +#if PG_VERSION_NUM >= 180000 + amroutine->amcanhash = false; + amroutine->amconsistentequality = false; + amroutine->amconsistentordering = false; +#endif amroutine->amcanbackward = false; /* can change direction mid-scan */ amroutine->amcanunique = false; amroutine->amcanmulticol = false; @@ -291,6 +304,9 @@ hnswhandler(PG_FUNCTION_ARGS) amroutine->amvacuumcleanup = hnswvacuumcleanup; amroutine->amcanreturn = NULL; amroutine->amcostestimate = hnswcostestimate; +#if PG_VERSION_NUM >= 180000 + amroutine->amgettreeheight = NULL; +#endif amroutine->amoptions = hnswoptions; amroutine->amproperty = NULL; /* TODO AMPROP_DISTANCE_ORDERABLE */ amroutine->ambuildphasename = hnswbuildphasename; @@ -311,5 +327,10 @@ hnswhandler(PG_FUNCTION_ARGS) amroutine->aminitparallelscan = NULL; amroutine->amparallelrescan = NULL; +#if PG_VERSION_NUM >= 180000 + amroutine->amtranslatestrategy = NULL; + amroutine->amtranslatecmptype = NULL; +#endif + PG_RETURN_POINTER(amroutine); } diff --git a/src/hnswbuild.c b/src/hnswbuild.c index b667478b6..03f0ef4a4 100644 --- a/src/hnswbuild.c +++ b/src/hnswbuild.c @@ -54,6 +54,10 @@ #include "utils/datum.h" #include "utils/memutils.h" +#if PG_VERSION_NUM >= 160000 +#include "varatt.h" +#endif + #if PG_VERSION_NUM >= 140000 #include "utils/backend_progress.h" #else @@ -398,7 +402,7 @@ UpdateNeighborsInMemory(char *base, HnswSupport * support, HnswElement e, int m) * Update graph in memory */ static void -UpdateGraphInMemory(HnswSupport * support, HnswElement element, int m, int efConstruction, HnswElement entryPoint, HnswBuildState * buildstate) +UpdateGraphInMemory(HnswSupport * support, HnswElement element, int m, HnswElement entryPoint, HnswBuildState * buildstate) { HnswGraph *graph = buildstate->graph; char *base = buildstate->hnswarea; @@ -460,7 +464,7 @@ InsertTupleInMemory(HnswBuildState * buildstate, HnswElement element) HnswFindElementNeighbors(base, element, entryPoint, NULL, support, m, efConstruction, false); /* Update graph in memory */ - UpdateGraphInMemory(support, element, m, efConstruction, entryPoint, buildstate); + UpdateGraphInMemory(support, element, m, entryPoint, buildstate); /* Release entry lock */ LWLockRelease(entryLock); @@ -1054,7 +1058,7 @@ ComputeParallelWorkers(Relation heap, Relation index) * Build graph */ static void -BuildGraph(HnswBuildState * buildstate, ForkNumber forkNum) +BuildGraph(HnswBuildState * buildstate) { int parallel_workers = 0; @@ -1102,7 +1106,7 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo, InitBuildState(buildstate, heap, index, indexInfo, forkNum); - BuildGraph(buildstate, forkNum); + BuildGraph(buildstate); if (RelationNeedsWAL(index) || forkNum == INIT_FORKNUM) log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocksInFork(index, forkNum), true); diff --git a/src/hnswinsert.c b/src/hnswinsert.c index a5fac4eda..a4d288506 100644 --- a/src/hnswinsert.c +++ b/src/hnswinsert.c @@ -9,6 +9,10 @@ #include "utils/datum.h" #include "utils/memutils.h" +#if PG_VERSION_NUM >= 160000 +#include "varatt.h" +#endif + /* * Get the insert page */ @@ -660,7 +664,7 @@ FindDuplicateOnDisk(Relation index, HnswElement element, bool building) * Update graph on disk */ static void -UpdateGraphOnDisk(Relation index, HnswSupport * support, HnswElement element, int m, int efConstruction, HnswElement entryPoint, bool building) +UpdateGraphOnDisk(Relation index, HnswSupport * support, HnswElement element, int m, HnswElement entryPoint, bool building) { BlockNumber newInsertPage = InvalidBlockNumber; @@ -728,7 +732,7 @@ HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPo HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, false); /* Update graph on disk */ - UpdateGraphOnDisk(index, support, element, m, efConstruction, entryPoint, building); + UpdateGraphOnDisk(index, support, element, m, entryPoint, building); /* Release lock */ UnlockPage(index, HNSW_UPDATE_LOCK, lockmode); diff --git a/src/hnswscan.c b/src/hnswscan.c index 955998a52..5c526f4a6 100644 --- a/src/hnswscan.c +++ b/src/hnswscan.c @@ -193,6 +193,10 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir) /* Count index scan for stats */ pgstat_count_index_scan(scan->indexRelation); +#if PG_VERSION_NUM >= 180000 + if (scan->instrument) + scan->instrument->nsearches++; +#endif /* Safety check */ if (scan->orderByData == NULL) diff --git a/src/hnswutils.c b/src/hnswutils.c index c52d2c78a..8e2a42c1d 100644 --- a/src/hnswutils.c +++ b/src/hnswutils.c @@ -15,6 +15,10 @@ #include "utils/memdebug.h" #include "utils/rel.h" +#if PG_VERSION_NUM >= 160000 +#include "varatt.h" +#endif + #if PG_VERSION_NUM < 170000 static inline uint64 murmurhash64(uint64 data) diff --git a/src/hnswvacuum.c b/src/hnswvacuum.c index 2f7b2f372..3a8ee26c0 100644 --- a/src/hnswvacuum.c +++ b/src/hnswvacuum.c @@ -9,6 +9,10 @@ #include "storage/lmgr.h" #include "utils/memutils.h" +#if PG_VERSION_NUM >= 160000 +#include "varatt.h" +#endif + #if PG_VERSION_NUM >= 180000 #define vacuum_delay_point() vacuum_delay_point(false) #endif diff --git a/src/ivfbuild.c b/src/ivfbuild.c index 944f07b55..7166b7da3 100644 --- a/src/ivfbuild.c +++ b/src/ivfbuild.c @@ -20,6 +20,10 @@ #include "utils/memutils.h" #include "vector.h" +#if PG_VERSION_NUM >= 160000 +#include "varatt.h" +#endif + #if PG_VERSION_NUM >= 140000 #include "utils/backend_progress.h" #else @@ -138,7 +142,7 @@ SampleRows(IvfflatBuildState * buildstate) * Add tuple to sort */ static void -AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState * buildstate) +AddTupleToSort(ItemPointer tid, Datum *values, IvfflatBuildState * buildstate) { double distance; double minDistance = DBL_MAX; @@ -215,7 +219,7 @@ BuildCallback(Relation index, ItemPointer tid, Datum *values, oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx); /* Add tuple to sort */ - AddTupleToSort(index, tid, values, buildstate); + AddTupleToSort(tid, values, buildstate); /* Reset memory context */ MemoryContextSwitchTo(oldCtx); @@ -470,8 +474,8 @@ CreateMetaPage(Relation index, int dimensions, int lists, ForkNumber forkNum) * Create list pages */ static void -CreateListPages(Relation index, VectorArray centers, int dimensions, - int lists, ForkNumber forkNum, ListInfo * *listInfo) +CreateListPages(Relation index, VectorArray centers, int lists, + ForkNumber forkNum, ListInfo * *listInfo) { Buffer buf; Page page; @@ -1004,7 +1008,7 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo, /* Create pages */ CreateMetaPage(index, buildstate->dimensions, buildstate->lists, forkNum); - CreateListPages(index, buildstate->centers, buildstate->dimensions, buildstate->lists, forkNum, &buildstate->listInfo); + CreateListPages(index, buildstate->centers, buildstate->lists, forkNum, &buildstate->listInfo); CreateEntryPages(buildstate, forkNum); /* Write WAL for initialization fork since GenericXLog functions do not */ @@ -1023,6 +1027,10 @@ ivfflatbuild(Relation heap, Relation index, IndexInfo *indexInfo) IndexBuildResult *result; IvfflatBuildState buildstate; +#ifdef IVFFLAT_BENCH + SeedRandom(42); +#endif + BuildIndex(heap, index, indexInfo, &buildstate, MAIN_FORKNUM); result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); diff --git a/src/ivfflat.c b/src/ivfflat.c index 9e8370ffe..31c2f7d5e 100644 --- a/src/ivfflat.c +++ b/src/ivfflat.c @@ -92,7 +92,7 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Relation index; /* Never use index without order */ - if (path->indexorderbys == NULL) + if (path->indexorderbys == NIL) { *indexStartupCost = get_float8_infinity(); *indexTotalCost = get_float8_infinity(); @@ -186,6 +186,11 @@ ivfflathandler(PG_FUNCTION_ARGS) amroutine->amoptsprocnum = 0; amroutine->amcanorder = false; amroutine->amcanorderbyop = true; +#if PG_VERSION_NUM >= 180000 + amroutine->amcanhash = false; + amroutine->amconsistentequality = false; + amroutine->amconsistentordering = false; +#endif amroutine->amcanbackward = false; /* can change direction mid-scan */ amroutine->amcanunique = false; amroutine->amcanmulticol = false; @@ -218,6 +223,9 @@ ivfflathandler(PG_FUNCTION_ARGS) amroutine->amvacuumcleanup = ivfflatvacuumcleanup; amroutine->amcanreturn = NULL; /* tuple not included in heapsort */ amroutine->amcostestimate = ivfflatcostestimate; +#if PG_VERSION_NUM >= 180000 + amroutine->amgettreeheight = NULL; +#endif amroutine->amoptions = ivfflatoptions; amroutine->amproperty = NULL; /* TODO AMPROP_DISTANCE_ORDERABLE */ amroutine->ambuildphasename = ivfflatbuildphasename; @@ -238,5 +246,10 @@ ivfflathandler(PG_FUNCTION_ARGS) amroutine->aminitparallelscan = NULL; amroutine->amparallelrescan = NULL; +#if PG_VERSION_NUM >= 180000 + amroutine->amtranslatestrategy = NULL; + amroutine->amtranslatecmptype = NULL; +#endif + PG_RETURN_POINTER(amroutine); } diff --git a/src/ivfflat.h b/src/ivfflat.h index c296b6677..c4e64e1dc 100644 --- a/src/ivfflat.h +++ b/src/ivfflat.h @@ -13,6 +13,10 @@ #include "utils/tuplesort.h" #include "vector.h" +#if PG_VERSION_NUM >= 160000 +#include "varatt.h" +#endif + #if PG_VERSION_NUM >= 150000 #include "common/pg_prng.h" #endif @@ -73,9 +77,11 @@ #if PG_VERSION_NUM >= 150000 #define RandomDouble() pg_prng_double(&pg_global_prng_state) #define RandomInt() pg_prng_uint32(&pg_global_prng_state) +#define SeedRandom(seed) pg_prng_seed(&pg_global_prng_state, seed) #else #define RandomDouble() (((double) random()) / MAX_RANDOM_VALUE) #define RandomInt() random() +#define SeedRandom(seed) srandom(seed) #endif /* Variables */ diff --git a/src/ivfinsert.c b/src/ivfinsert.c index 014c9be82..ebc61e49d 100644 --- a/src/ivfinsert.c +++ b/src/ivfinsert.c @@ -65,7 +65,7 @@ FindInsertPage(Relation index, Datum *values, BlockNumber *insertPage, ListInfo * Insert a tuple into the index */ static void -InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel) +InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid) { const IvfflatTypeInfo *typeInfo = IvfflatGetTypeInfo(index); IndexTuple itup; @@ -204,7 +204,7 @@ ivfflatinsert(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, oldCtx = MemoryContextSwitchTo(insertCtx); /* Insert tuple */ - InsertTuple(index, values, isnull, heap_tid, heap); + InsertTuple(index, values, isnull, heap_tid); /* Delete memory context */ MemoryContextSwitchTo(oldCtx); diff --git a/src/ivfkmeans.c b/src/ivfkmeans.c index 4b6d14f1a..9c79e64ad 100644 --- a/src/ivfkmeans.c +++ b/src/ivfkmeans.c @@ -13,6 +13,10 @@ #include "utils/memutils.h" #include "vector.h" +#if PG_VERSION_NUM >= 160000 +#include "varatt.h" +#endif + /* * Initialize with kmeans++ * diff --git a/src/ivfscan.c b/src/ivfscan.c index 6cc5d2efd..b42ada15d 100644 --- a/src/ivfscan.c +++ b/src/ivfscan.c @@ -355,6 +355,10 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir) /* Count index scan for stats */ pgstat_count_index_scan(scan->indexRelation); +#if PG_VERSION_NUM >= 180000 + if (scan->instrument) + scan->instrument->nsearches++; +#endif /* Safety check */ if (scan->orderByData == NULL) diff --git a/src/ivfutils.c b/src/ivfutils.c index da241ee0a..9596391be 100644 --- a/src/ivfutils.c +++ b/src/ivfutils.c @@ -259,8 +259,8 @@ VectorUpdateCenter(Pointer v, int dimensions, float *x) SET_VARSIZE(vec, VECTOR_SIZE(dimensions)); vec->dim = dimensions; - for (int k = 0; k < dimensions; k++) - vec->x[k] = x[k]; + for (int i = 0; i < dimensions; i++) + vec->x[i] = x[i]; } static void @@ -271,8 +271,8 @@ HalfvecUpdateCenter(Pointer v, int dimensions, float *x) SET_VARSIZE(vec, HALFVEC_SIZE(dimensions)); vec->dim = dimensions; - for (int k = 0; k < dimensions; k++) - vec->x[k] = Float4ToHalfUnchecked(x[k]); + for (int i = 0; i < dimensions; i++) + vec->x[i] = Float4ToHalfUnchecked(x[i]); } static void @@ -284,29 +284,33 @@ BitUpdateCenter(Pointer v, int dimensions, float *x) SET_VARSIZE(vec, VARBITTOTALLEN(dimensions)); VARBITLEN(vec) = dimensions; - for (uint32 k = 0; k < VARBITBYTES(vec); k++) - nx[k] = 0; + for (uint32 i = 0; i < VARBITBYTES(vec); i++) + nx[i] = 0; - for (int k = 0; k < dimensions; k++) - nx[k / 8] |= (x[k] > 0.5 ? 1 : 0) << (7 - (k % 8)); + for (int i = 0; i < dimensions; i++) + nx[i / 8] |= (x[i] > 0.5 ? 1 : 0) << (7 - (i % 8)); } static void VectorSumCenter(Pointer v, float *x) { Vector *vec = (Vector *) v; + int dim = vec->dim; - for (int k = 0; k < vec->dim; k++) - x[k] += vec->x[k]; + /* Auto-vectorized */ + for (int i = 0; i < dim; i++) + x[i] += vec->x[i]; } static void HalfvecSumCenter(Pointer v, float *x) { HalfVector *vec = (HalfVector *) v; + int dim = vec->dim; - for (int k = 0; k < vec->dim; k++) - x[k] += HalfToFloat4(vec->x[k]); + /* Auto-vectorized on aarch64 */ + for (int i = 0; i < dim; i++) + x[i] += HalfToFloat4(vec->x[i]); } static void @@ -314,8 +318,8 @@ BitSumCenter(Pointer v, float *x) { VarBit *vec = (VarBit *) v; - for (int k = 0; k < VARBITLEN(vec); k++) - x[k] += (float) (((VARBITS(vec)[k / 8]) >> (7 - (k % 8))) & 0x01); + for (int i = 0; i < VARBITLEN(vec); i++) + x[i] += (float) (((VARBITS(vec)[i / 8]) >> (7 - (i % 8))) & 0x01); } /* diff --git a/src/vector.c b/src/vector.c index a5b2aac36..03f70d601 100644 --- a/src/vector.c +++ b/src/vector.c @@ -35,7 +35,11 @@ #define VECTOR_TARGET_CLONES #endif +#if PG_VERSION_NUM >= 180000 +PG_MODULE_MAGIC_EXT(.name = "vector",.version = "0.8.1"); +#else PG_MODULE_MAGIC; +#endif /* * Initialize index options and variables @@ -920,11 +924,13 @@ vector_concat(PG_FUNCTION_ARGS) CheckDim(dim); result = InitVector(dim); - for (int i = 0; i < a->dim; i++) + /* Auto-vectorized */ + for (int i = 0, imax = a->dim; i < imax; i++) result->x[i] = a->x[i]; - for (int i = 0; i < b->dim; i++) - result->x[i + a->dim] = b->x[i]; + /* Auto-vectorized */ + for (int i = 0, imax = b->dim, start = a->dim; i < imax; i++) + result->x[i + start] = b->x[i]; PG_RETURN_POINTER(result); } @@ -940,8 +946,21 @@ binary_quantize(PG_FUNCTION_ARGS) float *ax = a->x; VarBit *result = InitBitVector(a->dim); unsigned char *rx = VARBITS(result); + int i = 0; + int count = (a->dim / 8) * 8; - for (int i = 0; i < a->dim; i++) + /* Auto-vectorized */ + for (; i < count; i += 8) + { + unsigned char result_byte = 0; + + for (int j = 0; j < 8; j++) + result_byte |= (ax[i + j] > 0) << (7 - j); + + rx[i / 8] = result_byte; + } + + for (; i < a->dim; i++) rx[i / 8] |= (ax[i] > 0) << (7 - (i % 8)); PG_RETURN_VARBIT_P(result); diff --git a/test/expected/halfvec.out b/test/expected/halfvec.out index a3ce8931f..c3856c512 100644 --- a/test/expected/halfvec.out +++ b/test/expected/halfvec.out @@ -378,6 +378,24 @@ SELECT '[0,0]'::halfvec <-> '[3,4]'; 5 (1 row) +SELECT l2_distance('[501]'::halfvec, '[1]'); + l2_distance +------------- + 500 +(1 row) + +SELECT l2_distance('[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); + l2_distance +------------- + 0 +(1 row) + +SELECT l2_distance('[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); + l2_distance +------------- + 6 +(1 row) + SELECT inner_product('[1,2]'::halfvec, '[3,4]'); inner_product --------------- @@ -404,6 +422,24 @@ SELECT '[1,2]'::halfvec <#> '[3,4]'; -11 (1 row) +SELECT inner_product('[50000,50000]'::halfvec, '[1,1]'); + inner_product +--------------- + 99968 +(1 row) + +SELECT inner_product('[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); + inner_product +--------------- + 36 +(1 row) + +SELECT inner_product('[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); + inner_product +--------------- + 72 +(1 row) + SELECT cosine_distance('[1,2]'::halfvec, '[2,4]'); cosine_distance ----------------- @@ -466,6 +502,24 @@ SELECT '[1,2]'::halfvec <=> '[2,4]'; 0 (1 row) +SELECT cosine_distance('[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); + cosine_distance +----------------- + 0 +(1 row) + +SELECT cosine_distance('[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); + cosine_distance +----------------- + 0 +(1 row) + +SELECT cosine_distance('[1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0]'::halfvec, '[0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]'); + cosine_distance +----------------- + 1 +(1 row) + SELECT l1_distance('[0,0]'::halfvec, '[3,4]'); l1_distance ------------- @@ -498,6 +552,18 @@ SELECT '[0,0]'::halfvec <+> '[3,4]'; 7 (1 row) +SELECT l1_distance('[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); + l1_distance +------------- + 0 +(1 row) + +SELECT l1_distance('[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); + l1_distance +------------- + 36 +(1 row) + SELECT l2_normalize('[3,4]'::halfvec); l2_normalize ------------------------ @@ -540,6 +606,12 @@ SELECT binary_quantize('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::halfvec) 01001110101 (1 row) +SELECT binary_quantize('[1,2,3,-4,5,6,-7,8,1,-2,-3,4,5,-6,7,8,-1,2,3]'::halfvec); + binary_quantize +--------------------- + 1110110110011011011 +(1 row) + SELECT subvector('[1,2,3,4,5]'::halfvec, 1, 3); subvector ----------- diff --git a/test/expected/vector_type.out b/test/expected/vector_type.out index 674865822..f4c85d035 100644 --- a/test/expected/vector_type.out +++ b/test/expected/vector_type.out @@ -576,6 +576,12 @@ SELECT binary_quantize('[0,0.1,-0.2,-0.3,0.4,0.5,0.6,-0.7,0.8,-0.9,1]'::vector); 01001110101 (1 row) +SELECT binary_quantize('[1,2,3,-4,5,6,-7,8,1,-2,-3,4,5,-6,7,8,-1,2,3]'::vector); + binary_quantize +--------------------- + 1110110110011011011 +(1 row) + SELECT subvector('[1,2,3,4,5]'::vector, 1, 3); subvector ----------- diff --git a/test/sql/halfvec.sql b/test/sql/halfvec.sql index 1a3fd1b82..744d03868 100644 --- a/test/sql/halfvec.sql +++ b/test/sql/halfvec.sql @@ -87,12 +87,18 @@ SELECT l2_distance('[0,0]'::halfvec, '[0,1]'); SELECT l2_distance('[1,2]'::halfvec, '[3]'); SELECT l2_distance('[1,1,1,1,1,1,1,1,1]'::halfvec, '[1,1,1,1,1,1,1,4,5]'); SELECT '[0,0]'::halfvec <-> '[3,4]'; +SELECT l2_distance('[501]'::halfvec, '[1]'); +SELECT l2_distance('[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); +SELECT l2_distance('[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); SELECT inner_product('[1,2]'::halfvec, '[3,4]'); SELECT inner_product('[1,2]'::halfvec, '[3]'); SELECT inner_product('[65504]'::halfvec, '[65504]'); SELECT inner_product('[1,1,1,1,1,1,1,1,1]'::halfvec, '[1,2,3,4,5,6,7,8,9]'); SELECT '[1,2]'::halfvec <#> '[3,4]'; +SELECT inner_product('[50000,50000]'::halfvec, '[1,1]'); +SELECT inner_product('[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); +SELECT inner_product('[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); SELECT cosine_distance('[1,2]'::halfvec, '[2,4]'); SELECT cosine_distance('[1,2]'::halfvec, '[0,0]'); @@ -105,6 +111,9 @@ 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 cosine_distance('[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); +SELECT cosine_distance('[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); +SELECT cosine_distance('[1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0]'::halfvec, '[0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]'); SELECT l1_distance('[0,0]'::halfvec, '[3,4]'); SELECT l1_distance('[0,0]'::halfvec, '[0,1]'); @@ -112,6 +121,8 @@ SELECT l1_distance('[1,2]'::halfvec, '[3]'); SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[1,2,3,4,5,6,7,8,9]'); SELECT l1_distance('[1,2,3,4,5,6,7,8,9]'::halfvec, '[0,3,2,5,4,7,6,9,8]'); SELECT '[0,0]'::halfvec <+> '[3,4]'; +SELECT l1_distance('[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); +SELECT l1_distance('[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]'::halfvec, '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]'); SELECT l2_normalize('[3,4]'::halfvec); SELECT l2_normalize('[3,0]'::halfvec); @@ -121,6 +132,7 @@ 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 binary_quantize('[1,2,3,-4,5,6,-7,8,1,-2,-3,4,5,-6,7,8,-1,2,3]'::halfvec); SELECT subvector('[1,2,3,4,5]'::halfvec, 1, 3); SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 2); diff --git a/test/sql/vector_type.sql b/test/sql/vector_type.sql index 088b040aa..086a39bcf 100644 --- a/test/sql/vector_type.sql +++ b/test/sql/vector_type.sql @@ -128,6 +128,7 @@ 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 binary_quantize('[1,2,3,-4,5,6,-7,8,1,-2,-3,4,5,-6,7,8,-1,2,3]'::vector); SELECT subvector('[1,2,3,4,5]'::vector, 1, 3); SELECT subvector('[1,2,3,4,5]'::vector, 3, 2); diff --git a/vector.control b/vector.control index 7bfc0f1fa..2ad02286a 100644 --- a/vector.control +++ b/vector.control @@ -1,4 +1,4 @@ comment = 'vector data type and ivfflat and hnsw access methods' -default_version = '0.8.0' +default_version = '0.8.1' module_pathname = '$libdir/vector' relocatable = true