Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
653 changes: 653 additions & 0 deletions .github/workflows/09-release-build.yml

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ bin/*
lib/*
var/*
venv*
.venv*
tests/integration/conf/*
tests/de_integration/conf/*
**/__pycache__/*
Expand All @@ -26,6 +27,31 @@ dist
html
*.lcov.info

# IDE / Editor
.vscode/
.idea/
*.swp
*.swo
*~
*.iml
.project
.classpath
.settings/

# ccache / sccache
.ccache/
.sccache/

# Python
*.egg-info/
*.egg
.eggs/
.pytest_cache/
.ruff_cache/
.mypy_cache/
.pyc
*.pyc

# Compiled extension modules must never live in the source package: they are
# built by CMake and installed into the wheel. A stray copy left here gets
# bundled verbatim by scikit-build's wheel.packages and breaks the wheel
Expand Down
47 changes: 47 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,35 @@ cmake_policy(SET CMP0077 NEW)
project(zvec)
set(CC_CXX_STANDARD 17)

# Compiler cache setup (ccache / sccache) — speeds up incremental builds
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
message(STATUS "Using ccache: ${CCACHE_PROGRAM}")
else()
find_program(SCCACHE_PROGRAM sccache)
if(SCCACHE_PROGRAM)
set(CMAKE_C_COMPILER_LAUNCHER "${SCCACHE_PROGRAM}")
set(CMAKE_CXX_COMPILER_LAUNCHER "${SCCACHE_PROGRAM}")
message(STATUS "Using sccache: ${SCCACHE_PROGRAM}")
endif()
endif()

# Link-Time Optimization (LTO) for Release builds
option(ENABLE_LTO "Enable Link-Time Optimization in Release builds" OFF)
if(ENABLE_LTO AND NOT MSVC AND NOT IOS AND NOT ANDROID)
include(CheckIPOSupported)
check_ipo_supported(RESULT LTO_SUPPORTED OUTPUT LTO_ERROR)
if(LTO_SUPPORTED)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
message(STATUS "LTO enabled for Release/RelWithDebInfo builds")
else()
message(WARNING "LTO not supported: ${LTO_ERROR}")
endif()
endif()

if(MSVC)
set(INTTYPES_FORMAT VC7)
# Use /Z7 (embed debug info in .obj) instead of /Zi (separate .pdb) to enable sccache/ccache.
Expand Down Expand Up @@ -38,6 +67,24 @@ if(MSVC)
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror=return-type")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror=return-type")

# Release build optimizations
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
# -O3 is generally safe and provides better performance than -O2
# for numerically intensive code like vector search
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")

# Function sections + data sections + gc-sections reduces binary size
add_compile_options(-ffunction-sections -fdata-sections)
if(NOT APPLE AND NOT IOS AND NOT ANDROID)
add_link_options(-Wl,--gc-sections)
endif()
endif()
endif()
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT IOS)
Expand Down
39 changes: 39 additions & 0 deletions cmake/zvecConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# zvecConfig.cmake.in - CMake package configuration template
# This file is processed by configure_package_config_file() to generate
# the final zvecConfig.cmake installed with the library.

@PACKAGE_INIT@

# Require CMake 3.13 or newer
cmake_minimum_required(VERSION 3.13)

# Set zvec root directory
set(ZVEC_ROOT_DIR "@PACKAGE_CMAKE_INSTALL_PREFIX@")

# Include imported targets (shared libraries)
include("@PACKAGE_CMAKE_INSTALL_LIBDIR@/cmake/zvec/zvec_c_apiTargets.cmake" OPTIONAL RESULT_VARIABLE _zvec_c_api_targets)
if(_zvec_c_api_targets)
message(STATUS "Loaded zvec_c_api targets from ${_zvec_c_api_targets}")
endif()

# Alias for convenience (zvec::zvec -> zvec::zvec_c_api)
if(TARGET zvec::zvec_c_api AND NOT TARGET zvec::zvec)
add_library(zvec::zvec ALIAS zvec::zvec_c_api)
message(STATUS "Created alias zvec::zvec -> zvec::zvec_c_api")
endif()

# Helper function to print zvec info
function(zvec_info)
message(STATUS "zvec version: @ZVEC_VERSION_MAJOR@.@ZVEC_VERSION_MINOR@.@ZVEC_VERSION_PATCH@")
message(STATUS "zvec include dir: ${ZVEC_ROOT_DIR}/@CMAKE_INSTALL_INCLUDEDIR@")
message(STATUS "zvec library dir: ${ZVEC_ROOT_DIR}/@CMAKE_INSTALL_LIBDIR@")
endfunction()

# Mark package as found
set(zvec_FOUND TRUE)
set(ZVEC_FOUND TRUE)

# Provide legacy variables for backwards compatibility
set(ZVEC_INCLUDE_DIRS "${ZVEC_ROOT_DIR}/@CMAKE_INSTALL_INCLUDEDIR@")
set(ZVEC_LIBRARY_DIRS "${ZVEC_ROOT_DIR}/@CMAKE_INSTALL_LIBDIR@")
set(ZVEC_LIBRARIES zvec::zvec_c_api)
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ BUILD_TOOLS = "OFF"
BUILD_PYTHON_BINDINGS = "ON"
#CMAKE_VERBOSE_MAKEFILE = "ON"

# Enable ccache if available (speeds up rebuilds)
[tool.scikit-build.cmake.env]
# CMake will auto-detect ccache/sccache via the CMAKE_CXX_COMPILER_LAUNCHER
# set in the top-level CMakeLists.txt.

# Setuptools config for test pypi
[tool.setuptools_scm]
local_scheme = "no-local-version"
Expand Down
56 changes: 56 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ function(zvec_add_all_in_one_shared TARGET_NAME OUTPUT_NAME)
endif()

install(TARGETS ${TARGET_NAME}
EXPORT ${TARGET_NAME}Targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
Expand Down Expand Up @@ -169,3 +170,58 @@ if(BUILD_ZVEC_SHARED)
zvec_turbo
)
endif()

# =============================================================================
# CMake Package Configuration (for find_package(zvec))
# =============================================================================

# Only install package config if shared libraries are built
if(BUILD_ZVEC_CORE_SHARED OR BUILD_ZVEC_SHARED)
include(CMakePackageConfigHelpers)

# Generate package version file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfigVersion.cmake"
VERSION "${ZVEC_VERSION_MAJOR}.${ZVEC_VERSION_MINOR}.${ZVEC_VERSION_PATCH}"
COMPATIBILITY SameMajorVersion
)

# Generate package config from template
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/zvecConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)

# Install package config files
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)
endif()

# Export targets from build tree (for find_package(zvec) without install)
if(BUILD_ZVEC_CORE_SHARED)
export(EXPORT zvec_core_sharedTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/zvec_core_sharedTargets.cmake"
NAMESPACE zvec::
)
install(EXPORT zvec_core_sharedTargets
FILE zvec_core_sharedTargets.cmake
NAMESPACE zvec::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)
endif()

if(BUILD_ZVEC_SHARED)
export(EXPORT zvec_sharedTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/zvec_sharedTargets.cmake"
NAMESPACE zvec::
)
install(EXPORT zvec_sharedTargets
FILE zvec_sharedTargets.cmake
NAMESPACE zvec::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)
endif()
40 changes: 40 additions & 0 deletions src/binding/c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ endif()

# Install shared library
install(TARGETS zvec_c_api
EXPORT zvec_c_apiTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
Expand All @@ -254,3 +255,42 @@ install(TARGETS zvec_c_api
install(FILES ${PROJECT_SOURCE_DIR}/src/include/zvec/c_api.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/zvec
)

# =============================================================================
# CMake Package Configuration (for find_package(zvec))
# =============================================================================

# Export targets for use from build tree
export(EXPORT zvec_c_apiTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/zvec_c_apiTargets.cmake"
NAMESPACE zvec::
)

# Install targets for use from install tree
install(EXPORT zvec_c_apiTargets
FILE zvec_c_apiTargets.cmake
NAMESPACE zvec::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)

# Generate & install package version file
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfigVersion.cmake"
VERSION "${ZVEC_VERSION_MAJOR}.${ZVEC_VERSION_MINOR}.${ZVEC_VERSION_PATCH}"
COMPATIBILITY SameMajorVersion
)

# Generate package config from template
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/zvecConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)

# Install package config files
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)
94 changes: 94 additions & 0 deletions src/binding/c/c_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3660,6 +3660,45 @@ zvec_error_code_t zvec_doc_add_field_by_struct(zvec_doc_t *doc,
return ZVEC_OK;)
}

// ---------------------------------------------------------------------------
// Sparse Vector Document Field Helper (Issue #443)
// ---------------------------------------------------------------------------

zvec_error_code_t zvec_doc_set_sparse_vector(
zvec_doc_t *doc,
const char *field_name,
const uint32_t *indices,
const float *values,
size_t count) {
if (!doc || !field_name) {
set_last_error("Invalid arguments: null pointer");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
if ((!indices || !values) && count > 0) {
set_last_error("Indices or values pointer is null while count > 0");
return ZVEC_ERROR_INVALID_ARGUMENT;
}

ZVEC_TRY_RETURN_ERROR(
"Failed to set sparse vector field",
auto *doc_ptr = reinterpret_cast<zvec::Doc *>(doc);
std::string name(field_name);

if (count == 0) {
// Set empty sparse vector (pair of empty vectors)
doc_ptr->set(name,
std::pair<std::vector<uint32_t>, std::vector<float>>{});
return ZVEC_OK;
}

std::vector<uint32_t> idx_vec(indices, indices + count);
std::vector<float> val_vec(values, values + count);
doc_ptr->set(name,
std::pair<std::vector<uint32_t>, std::vector<float>>{
std::move(idx_vec), std::move(val_vec)});
return ZVEC_OK;)
}

const char *zvec_doc_get_pk_pointer(const zvec_doc_t *doc) {
if (!doc) return nullptr;
auto doc_ptr = reinterpret_cast<const zvec::Doc *>(doc);
Expand Down Expand Up @@ -6092,6 +6131,61 @@ zvec_error_code_t zvec_multi_query_set_rerank_weighted(
return ZVEC_OK;
}

// ---------------------------------------------------------------------------
// Standalone Reranker API (Issue #443)
// ---------------------------------------------------------------------------
// Internal wrapper: holds a copy of RerankParams.
struct RerankerWrapper {
zvec::reranker::RerankParams params;
};

zvec_reranker_t *zvec_reranker_create_rrf(int rank_constant) {
ZVEC_TRY_RETURN_NULL("Failed to create RRF reranker",
auto *wrapper = new RerankerWrapper();
wrapper->params = zvec::reranker::RrfParams{rank_constant};
return reinterpret_cast<zvec_reranker_t *>(wrapper);)
return nullptr;
}

zvec_reranker_t *zvec_reranker_create_weighted(const double *weights,
size_t weight_count) {
if (!weights && weight_count > 0) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Weights pointer is null");
return nullptr;
}
ZVEC_TRY_RETURN_NULL(
"Failed to create Weighted reranker",
auto *wrapper = new RerankerWrapper();
wrapper->params = zvec::reranker::WeightedParams{
std::vector<double>(weights, weights + weight_count)};
return reinterpret_cast<zvec_reranker_t *>(wrapper);)
return nullptr;
}

void zvec_reranker_destroy(zvec_reranker_t *reranker) {
if (reranker) {
delete reinterpret_cast<RerankerWrapper *>(reranker);
}
}

zvec_error_code_t zvec_multi_query_set_reranker(
zvec_multi_query_t *query, const zvec_reranker_t *reranker) {
if (!query) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT, "Query pointer is null");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *mq = reinterpret_cast<zvec::MultiQuery *>(query);
if (!reranker) {
// Clear reranking by setting default RRF with default constant
mq->rerank = zvec::reranker::RrfParams{60};
return ZVEC_OK;
}
auto *wrapper = reinterpret_cast<const RerankerWrapper *>(reranker);
mq->rerank = wrapper->params;
return ZVEC_OK;
}

// =============================================================================
// MultiVectorQuery Implementation
// =============================================================================
Expand Down
Loading