feat: pass prefetch config (PO and PL) as search params (#482)
This commit is contained in:
parent
7c4571b8b9
commit
7a5f84e648
|
|
@ -325,13 +325,26 @@ class TestHnswQueryParam:
|
|||
assert param.is_using_refiner == False
|
||||
assert param.radius == 0
|
||||
assert param.is_linear == False
|
||||
assert param.prefetch_offset == 8
|
||||
assert param.prefetch_lines == 0
|
||||
|
||||
def test_custom(self):
|
||||
param = HnswQueryParam(ef=10, is_using_refiner=True, radius=30, is_linear=True)
|
||||
param = HnswQueryParam(
|
||||
ef=10,
|
||||
is_using_refiner=True,
|
||||
radius=30,
|
||||
is_linear=True,
|
||||
extra_params={
|
||||
"prefetch_offset": 16,
|
||||
"prefetch_lines": 4,
|
||||
},
|
||||
)
|
||||
assert param.ef == 10
|
||||
assert param.is_using_refiner == True
|
||||
assert param.radius == 30
|
||||
assert param.is_linear == True
|
||||
assert param.prefetch_offset == 16
|
||||
assert param.prefetch_lines == 4
|
||||
|
||||
def test_readonly_attributes(self):
|
||||
param = HnswQueryParam()
|
||||
|
|
|
|||
|
|
@ -274,16 +274,27 @@ class TestVamanaQueryParamSurface:
|
|||
assert q.radius == pytest.approx(0.0)
|
||||
assert q.is_linear is False
|
||||
assert q.is_using_refiner is False
|
||||
assert q.prefetch_offset == 8
|
||||
assert q.prefetch_lines == 0
|
||||
|
||||
def test_custom_construction(self):
|
||||
q = VamanaQueryParam(
|
||||
ef_search=300, radius=0.5, is_linear=True, is_using_refiner=True
|
||||
ef_search=300,
|
||||
radius=0.5,
|
||||
is_linear=True,
|
||||
is_using_refiner=True,
|
||||
extra_params={
|
||||
"prefetch_offset": 8,
|
||||
"prefetch_lines": 2,
|
||||
},
|
||||
)
|
||||
assert q.type == IndexType.VAMANA
|
||||
assert q.ef_search == 300
|
||||
assert q.radius == pytest.approx(0.5)
|
||||
assert q.is_linear is True
|
||||
assert q.is_using_refiner is True
|
||||
assert q.prefetch_offset == 8
|
||||
assert q.prefetch_lines == 2
|
||||
|
||||
def test_repr_contains_key_fields(self):
|
||||
text = repr(VamanaQueryParam(ef_search=128, radius=0.25))
|
||||
|
|
@ -302,7 +313,14 @@ class TestVamanaQueryParamSurface:
|
|||
|
||||
def test_pickle_roundtrip(self):
|
||||
original = VamanaQueryParam(
|
||||
ef_search=256, radius=0.3, is_linear=False, is_using_refiner=True
|
||||
ef_search=256,
|
||||
radius=0.3,
|
||||
is_linear=False,
|
||||
is_using_refiner=True,
|
||||
extra_params={
|
||||
"prefetch_offset": 4,
|
||||
"prefetch_lines": 3,
|
||||
},
|
||||
)
|
||||
restored = pickle.loads(pickle.dumps(original))
|
||||
assert restored.type == IndexType.VAMANA
|
||||
|
|
@ -310,6 +328,8 @@ class TestVamanaQueryParamSurface:
|
|||
assert restored.radius == pytest.approx(0.3)
|
||||
assert restored.is_linear is False
|
||||
assert restored.is_using_refiner is True
|
||||
assert restored.prefetch_offset == 4
|
||||
assert restored.prefetch_lines == 3
|
||||
|
||||
|
||||
class TestVamanaPublicNamespace:
|
||||
|
|
|
|||
|
|
@ -267,6 +267,12 @@ class HnswQueryParam(QueryParam):
|
|||
radius (float): Search radius for range queries. Default is 0.0.
|
||||
is_linear (bool): Force linear search. Default is False.
|
||||
is_using_refiner (bool, optional): Whether to use refiner for the query. Default is False.
|
||||
prefetch_offset (int, optional): Graph prefetch offset (PO) used by the
|
||||
HNSW fast path. ``0`` disables prefetching. Default is ``8``.
|
||||
Values are clamped to ``256``.
|
||||
prefetch_lines (int, optional): Number of 64B cache lines to prefetch
|
||||
per neighbour vector (PL). ``0`` (default) uses the auto-derived
|
||||
value ``ceil(vector_size/64)``. Values are clamped to ``256``.
|
||||
|
||||
Examples:
|
||||
>>> params = HnswQueryParam(ef=300)
|
||||
|
|
@ -282,6 +288,7 @@ class HnswQueryParam(QueryParam):
|
|||
radius: typing.SupportsFloat = 0.0,
|
||||
is_linear: bool = False,
|
||||
is_using_refiner: bool = False,
|
||||
extra_params: dict[str, int] = ...,
|
||||
) -> None:
|
||||
"""
|
||||
Constructs an HnswQueryParam instance.
|
||||
|
|
@ -292,6 +299,11 @@ class HnswQueryParam(QueryParam):
|
|||
radius (float, optional): Search radius for range queries. Default is 0.0.
|
||||
is_linear (bool, optional): Force linear search. Default is False.
|
||||
is_using_refiner (bool, optional): Whether to use refiner for the query. Default is False.
|
||||
extra_params (dict, optional): Additional search parameters. Supported keys:
|
||||
- ``prefetch_offset`` (int): Graph prefetch offset (PO).
|
||||
``0`` disables prefetching. Default is ``8``.
|
||||
- ``prefetch_lines`` (int): Number of 64B cache lines to prefetch
|
||||
per neighbour vector (PL). ``0`` (default) means auto-derive from vector size.
|
||||
"""
|
||||
def __repr__(self) -> str: ...
|
||||
def __setstate__(self, arg0: tuple) -> None: ...
|
||||
|
|
@ -301,6 +313,18 @@ class HnswQueryParam(QueryParam):
|
|||
int: Size of the dynamic candidate list during HNSW search.
|
||||
"""
|
||||
|
||||
@property
|
||||
def prefetch_offset(self) -> int:
|
||||
"""
|
||||
int: Graph prefetch offset used by the HNSW fast path.
|
||||
"""
|
||||
|
||||
@property
|
||||
def prefetch_lines(self) -> int:
|
||||
"""
|
||||
int: Override of prefetch cache lines per vector (0=auto).
|
||||
"""
|
||||
|
||||
class HnswRabitqIndexParam(VectorIndexParam):
|
||||
"""
|
||||
|
||||
|
|
@ -549,6 +573,110 @@ class IVFQueryParam(QueryParam):
|
|||
int: Number of inverted lists to search during IVF query.
|
||||
"""
|
||||
|
||||
class VamanaIndexParam(VectorIndexParam):
|
||||
"""
|
||||
Parameters for configuring a Vamana (DiskANN) index.
|
||||
|
||||
Attributes:
|
||||
metric_type (MetricType): Distance metric. Default is ``MetricType.IP``.
|
||||
max_degree (int): Maximum out-degree (R) of every node. Default is 64.
|
||||
search_list_size (int): Candidate list size during construction. Default is 100.
|
||||
alpha (float): RobustPrune alpha factor. Default is 1.2.
|
||||
saturate_graph (bool): Force every node to reach max_degree. Default is False.
|
||||
use_contiguous_memory (bool): Allocate contiguous memory arena. Default is False.
|
||||
use_id_map (bool): Reserved flag for id remapping. Default is False.
|
||||
quantize_type (QuantizeType): Vector quantization type. Default is ``QuantizeType.UNDEFINED``.
|
||||
|
||||
Examples:
|
||||
>>> params = VamanaIndexParam(metric_type=MetricType.COSINE, max_degree=64)
|
||||
"""
|
||||
def __getstate__(self) -> tuple: ...
|
||||
def __init__(
|
||||
self,
|
||||
metric_type: _zvec.typing.MetricType = ...,
|
||||
max_degree: typing.SupportsInt = 64,
|
||||
search_list_size: typing.SupportsInt = 100,
|
||||
alpha: typing.SupportsFloat = 1.2,
|
||||
saturate_graph: bool = False,
|
||||
use_contiguous_memory: bool = False,
|
||||
use_id_map: bool = False,
|
||||
quantize_type: _zvec.typing.QuantizeType = ...,
|
||||
) -> None: ...
|
||||
def __repr__(self) -> str: ...
|
||||
def __setstate__(self, arg0: tuple) -> None: ...
|
||||
def to_dict(self) -> dict: ...
|
||||
@property
|
||||
def max_degree(self) -> int:
|
||||
"""int: Maximum out-degree (R) of every node in the Vamana graph."""
|
||||
@property
|
||||
def search_list_size(self) -> int:
|
||||
"""int: Candidate list size during Vamana graph construction."""
|
||||
@property
|
||||
def alpha(self) -> float:
|
||||
"""float: Vamana RobustPrune alpha factor."""
|
||||
@property
|
||||
def saturate_graph(self) -> bool:
|
||||
"""bool: Whether to saturate every node to max_degree neighbors."""
|
||||
@property
|
||||
def use_contiguous_memory(self) -> bool:
|
||||
"""bool: Whether to allocate a single contiguous memory arena."""
|
||||
@property
|
||||
def use_id_map(self) -> bool:
|
||||
"""bool: Reserved flag for engine-level id remapping."""
|
||||
|
||||
class VamanaQueryParam(QueryParam):
|
||||
"""
|
||||
Query parameters for the Vamana (DiskANN) index.
|
||||
|
||||
Attributes:
|
||||
type (IndexType): Always ``IndexType.VAMANA``.
|
||||
ef_search (int): Size of the dynamic candidate list during search. Default is 200.
|
||||
radius (float): Search radius for range queries. Default is 0.0.
|
||||
is_linear (bool): Force linear search. Default is False.
|
||||
is_using_refiner (bool): Whether to use refiner. Default is False.
|
||||
prefetch_offset (int): Graph prefetch offset (PO). Default is 8.
|
||||
prefetch_lines (int): Cache lines to prefetch per vector (PL). Default is 0 (auto).
|
||||
|
||||
Examples:
|
||||
>>> params = VamanaQueryParam(ef_search=200)
|
||||
>>> print(params.ef_search)
|
||||
200
|
||||
"""
|
||||
def __getstate__(self) -> tuple: ...
|
||||
def __init__(
|
||||
self,
|
||||
ef_search: typing.SupportsInt = 200,
|
||||
radius: typing.SupportsFloat = 0.0,
|
||||
is_linear: bool = False,
|
||||
is_using_refiner: bool = False,
|
||||
extra_params: dict[str, int] = ...,
|
||||
) -> None:
|
||||
"""
|
||||
Constructs a VamanaQueryParam instance.
|
||||
|
||||
Args:
|
||||
ef_search (int, optional): Search-time candidate list size. Defaults to 200.
|
||||
radius (float, optional): Search radius for range queries. Default is 0.0.
|
||||
is_linear (bool, optional): Force linear search. Default is False.
|
||||
is_using_refiner (bool, optional): Whether to use refiner. Default is False.
|
||||
extra_params (dict, optional): Additional search parameters. Supported keys:
|
||||
- ``prefetch_offset`` (int): Graph prefetch offset (PO).
|
||||
``0`` disables prefetching. Default is ``8``.
|
||||
- ``prefetch_lines`` (int): Cache lines to prefetch per vector (PL).
|
||||
``0`` (default) means auto-derive from vector size.
|
||||
"""
|
||||
def __repr__(self) -> str: ...
|
||||
def __setstate__(self, arg0: tuple) -> None: ...
|
||||
@property
|
||||
def ef_search(self) -> int:
|
||||
"""int: Size of the dynamic candidate list during Vamana search."""
|
||||
@property
|
||||
def prefetch_offset(self) -> int:
|
||||
"""int: Graph prefetch offset used by the Vamana fast path."""
|
||||
@property
|
||||
def prefetch_lines(self) -> int:
|
||||
"""int: Override of prefetch cache lines per vector (0=auto)."""
|
||||
|
||||
class IndexOption:
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -1343,6 +1343,18 @@ zvec_index_params_t *zvec_index_params_create(zvec_index_type_t index_type) {
|
|||
false, // use_soar (default)
|
||||
zvec::QuantizeType::UNDEFINED);
|
||||
break;
|
||||
case ZVEC_INDEX_TYPE_VAMANA:
|
||||
cpp_params =
|
||||
new zvec::VamanaIndexParams(
|
||||
zvec::MetricType::L2, // metric_type
|
||||
zvec::core_interface::kDefaultVamanaMaxDegree,
|
||||
zvec::core_interface::kDefaultVamanaSearchListSize,
|
||||
zvec::core_interface::kDefaultVamanaAlpha,
|
||||
zvec::core_interface::kDefaultVamanaSaturateGraph,
|
||||
false, // use_contiguous_memory
|
||||
false, // use_id_map
|
||||
zvec::QuantizeType::UNDEFINED);
|
||||
break;
|
||||
case ZVEC_INDEX_TYPE_FLAT:
|
||||
default:
|
||||
cpp_params =
|
||||
|
|
@ -1546,6 +1558,55 @@ int zvec_index_params_get_hnsw_ef_construction(const zvec_index_params_t *params
|
|||
return hnsw_params->ef_construction();
|
||||
}
|
||||
|
||||
zvec_error_code_t zvec_index_params_set_vamana_params(
|
||||
zvec_index_params_t *params, int max_degree, int search_list_size,
|
||||
float alpha, bool saturate_graph, bool use_contiguous_memory) {
|
||||
if (!params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Invalid params or not Vamana index type");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
auto *cpp_params = reinterpret_cast<zvec::IndexParams *>(params);
|
||||
auto *vamana_params = dynamic_cast<zvec::VamanaIndexParams *>(cpp_params);
|
||||
if (!vamana_params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Invalid params or not Vamana index type");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
vamana_params->set_max_degree(max_degree);
|
||||
vamana_params->set_search_list_size(search_list_size);
|
||||
vamana_params->set_alpha(alpha);
|
||||
vamana_params->set_saturate_graph(saturate_graph);
|
||||
vamana_params->set_use_contiguous_memory(use_contiguous_memory);
|
||||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
zvec_error_code_t zvec_index_params_get_vamana_params(
|
||||
const zvec_index_params_t *params, int *out_max_degree,
|
||||
int *out_search_list_size, float *out_alpha, bool *out_saturate_graph,
|
||||
bool *out_use_contiguous_memory) {
|
||||
if (!params || !out_max_degree || !out_search_list_size || !out_alpha ||
|
||||
!out_saturate_graph || !out_use_contiguous_memory) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Invalid params or output pointer is null");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
auto *cpp_params = reinterpret_cast<const zvec::IndexParams *>(params);
|
||||
auto *vamana_params =
|
||||
dynamic_cast<const zvec::VamanaIndexParams *>(cpp_params);
|
||||
if (!vamana_params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Invalid params or not Vamana index type");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
*out_max_degree = vamana_params->max_degree();
|
||||
*out_search_list_size = vamana_params->search_list_size();
|
||||
*out_alpha = vamana_params->alpha();
|
||||
*out_saturate_graph = vamana_params->saturate_graph();
|
||||
*out_use_contiguous_memory = vamana_params->use_contiguous_memory();
|
||||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set IVF-specific parameters
|
||||
* @param params Index parameters (must be IVF type)
|
||||
|
|
@ -5146,6 +5207,102 @@ zvec_error_code_t zvec_vector_query_get_output_fields(const zvec_vector_query_t
|
|||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// VamanaQueryParams implementation - wrapper around zvec::VamanaQueryParams
|
||||
// =============================================================================
|
||||
|
||||
zvec_vamana_query_params_t *zvec_query_params_vamana_create(
|
||||
int ef_search, float radius, bool is_linear, bool is_using_refiner) {
|
||||
ZVEC_TRY_RETURN_NULL(
|
||||
"Failed to create VamanaQueryParams",
|
||||
auto *params = new zvec::VamanaQueryParams(ef_search, radius, is_linear,
|
||||
is_using_refiner);
|
||||
return reinterpret_cast<zvec_vamana_query_params_t *>(params);)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void zvec_query_params_vamana_destroy(zvec_vamana_query_params_t *params) {
|
||||
if (params) {
|
||||
delete reinterpret_cast<zvec::VamanaQueryParams *>(params);
|
||||
}
|
||||
}
|
||||
|
||||
zvec_error_code_t zvec_query_params_vamana_set_ef_search(
|
||||
zvec_vamana_query_params_t *params, int ef_search) {
|
||||
if (!params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Vamana query params pointer is null");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
auto *ptr = reinterpret_cast<zvec::VamanaQueryParams *>(params);
|
||||
ptr->set_ef_search(ef_search);
|
||||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
int zvec_query_params_vamana_get_ef_search(
|
||||
const zvec_vamana_query_params_t *params) {
|
||||
if (!params) return zvec::core_interface::kDefaultVamanaEfSearch;
|
||||
auto *ptr = reinterpret_cast<const zvec::VamanaQueryParams *>(params);
|
||||
return ptr->ef_search();
|
||||
}
|
||||
|
||||
zvec_error_code_t zvec_query_params_vamana_set_radius(
|
||||
zvec_vamana_query_params_t *params, float radius) {
|
||||
if (!params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Vamana query params pointer is null");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
auto *ptr = reinterpret_cast<zvec::VamanaQueryParams *>(params);
|
||||
ptr->set_radius(radius);
|
||||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
float zvec_query_params_vamana_get_radius(
|
||||
const zvec_vamana_query_params_t *params) {
|
||||
if (!params) return 0.0f;
|
||||
auto *ptr = reinterpret_cast<const zvec::VamanaQueryParams *>(params);
|
||||
return ptr->radius();
|
||||
}
|
||||
|
||||
zvec_error_code_t zvec_query_params_vamana_set_is_linear(
|
||||
zvec_vamana_query_params_t *params, bool is_linear) {
|
||||
if (!params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Vamana query params pointer is null");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
auto *ptr = reinterpret_cast<zvec::VamanaQueryParams *>(params);
|
||||
ptr->set_is_linear(is_linear);
|
||||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
bool zvec_query_params_vamana_get_is_linear(
|
||||
const zvec_vamana_query_params_t *params) {
|
||||
if (!params) return false;
|
||||
auto *ptr = reinterpret_cast<const zvec::VamanaQueryParams *>(params);
|
||||
return ptr->is_linear();
|
||||
}
|
||||
|
||||
zvec_error_code_t zvec_query_params_vamana_set_is_using_refiner(
|
||||
zvec_vamana_query_params_t *params, bool is_using_refiner) {
|
||||
if (!params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Vamana query params pointer is null");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
auto *ptr = reinterpret_cast<zvec::VamanaQueryParams *>(params);
|
||||
ptr->set_is_using_refiner(is_using_refiner);
|
||||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
bool zvec_query_params_vamana_get_is_using_refiner(
|
||||
const zvec_vamana_query_params_t *params) {
|
||||
if (!params) return false;
|
||||
auto *ptr = reinterpret_cast<const zvec::VamanaQueryParams *>(params);
|
||||
return ptr->is_using_refiner();
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Type-safe query params attachment functions (transfer ownership to
|
||||
// query object)
|
||||
|
|
@ -5237,6 +5394,23 @@ zvec_error_code_t zvec_vector_query_set_fts_params(
|
|||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
zvec_error_code_t zvec_vector_query_set_vamana_params(
|
||||
zvec_vector_query_t *query, zvec_vamana_query_params_t *vamana_params) {
|
||||
if (!query || !vamana_params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Query or Vamana params pointer is null");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto *query_ptr = reinterpret_cast<zvec::SearchQuery *>(query);
|
||||
auto *params_ptr =
|
||||
reinterpret_cast<zvec::VamanaQueryParams *>(vamana_params);
|
||||
|
||||
query_ptr->target_.query_params_.reset(params_ptr);
|
||||
|
||||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Fts payload implementation - wrapper around zvec::FtsClause (value type)
|
||||
// =============================================================================
|
||||
|
|
@ -5574,6 +5748,24 @@ zvec_error_code_t zvec_group_by_vector_query_set_flat_params(
|
|||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
zvec_error_code_t zvec_group_by_vector_query_set_vamana_params(
|
||||
zvec_group_by_vector_query_t *query,
|
||||
zvec_vamana_query_params_t *vamana_params) {
|
||||
if (!query || !vamana_params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Query or Vamana params pointer is null");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto *query_ptr = reinterpret_cast<zvec::GroupByVectorQuery *>(query);
|
||||
auto *params_ptr =
|
||||
reinterpret_cast<zvec::VamanaQueryParams *>(vamana_params);
|
||||
|
||||
query_ptr->target_.query_params_.reset(params_ptr);
|
||||
|
||||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Reranker Implementation
|
||||
// =============================================================================
|
||||
|
|
@ -5916,6 +6108,20 @@ zvec_error_code_t zvec_sub_query_set_flat_params(
|
|||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
zvec_error_code_t zvec_sub_query_set_vamana_params(
|
||||
zvec_sub_query_t *query, zvec_vamana_query_params_t *vamana_params) {
|
||||
if (!query || !vamana_params) {
|
||||
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
|
||||
"Sub-vector query or Vamana params pointer is null");
|
||||
return ZVEC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
auto *ptr = reinterpret_cast<zvec::SubQuery *>(query);
|
||||
auto *params_ptr =
|
||||
reinterpret_cast<zvec::VamanaQueryParams *>(vamana_params);
|
||||
ptr->target_.query_params_.reset(params_ptr);
|
||||
return ZVEC_OK;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Index Interface Implementation
|
||||
// =============================================================================
|
||||
|
|
|
|||
|
|
@ -1057,10 +1057,24 @@ Examples:
|
|||
{"type":"HNSW", "ef":300}
|
||||
)pbdoc");
|
||||
hnsw_params
|
||||
.def(py::init<int, float, bool, bool>(),
|
||||
.def(py::init([](int ef, float radius, bool is_linear,
|
||||
bool is_using_refiner, py::dict extra_params) {
|
||||
auto obj = std::make_shared<HnswQueryParams>(ef, radius, is_linear,
|
||||
is_using_refiner);
|
||||
if (extra_params.contains("prefetch_offset")) {
|
||||
obj->set_prefetch_offset(
|
||||
extra_params["prefetch_offset"].cast<uint32_t>());
|
||||
}
|
||||
if (extra_params.contains("prefetch_lines")) {
|
||||
obj->set_prefetch_lines(
|
||||
extra_params["prefetch_lines"].cast<uint32_t>());
|
||||
}
|
||||
return obj;
|
||||
}),
|
||||
py::arg("ef") = core_interface::kDefaultHnswEfSearch,
|
||||
py::arg("radius") = 0.0f, py::arg("is_linear") = false,
|
||||
py::arg("is_using_refiner") = false,
|
||||
py::arg("extra_params") = py::dict(),
|
||||
R"pbdoc(
|
||||
Constructs an HnswQueryParam instance.
|
||||
|
||||
|
|
@ -1070,10 +1084,29 @@ Args:
|
|||
radius (float, optional): Search radius for range queries. Default is 0.0.
|
||||
is_linear (bool, optional): Force linear search. Default is False.
|
||||
is_using_refiner (bool, optional): Whether to use refiner for the query. Default is False.
|
||||
extra_params (dict, optional): Additional search parameters. Supported keys:
|
||||
- ``prefetch_offset`` (int): Graph prefetch offset (PO).
|
||||
``0`` disables prefetching. Default is ``8``.
|
||||
Values are clamped to ``256``.
|
||||
- ``prefetch_lines`` (int): Number of 64B cache lines to prefetch
|
||||
per neighbour vector (PL). ``0`` (default) uses the auto-derived
|
||||
value ``ceil(vector_size/64)``. Values are clamped to ``256``.
|
||||
)pbdoc")
|
||||
.def_property_readonly(
|
||||
"ef", [](const HnswQueryParams &self) -> int { return self.ef(); },
|
||||
"int: Size of the dynamic candidate list during HNSW search.")
|
||||
.def_property_readonly(
|
||||
"prefetch_offset",
|
||||
[](const HnswQueryParams &self) -> uint32_t {
|
||||
return self.prefetch_offset();
|
||||
},
|
||||
"int: Graph prefetch offset used by the HNSW fast path.")
|
||||
.def_property_readonly(
|
||||
"prefetch_lines",
|
||||
[](const HnswQueryParams &self) -> uint32_t {
|
||||
return self.prefetch_lines();
|
||||
},
|
||||
"int: Override of prefetch cache lines per vector (0=auto).")
|
||||
.def("__repr__",
|
||||
[](const HnswQueryParams &self) -> std::string {
|
||||
return "{"
|
||||
|
|
@ -1083,20 +1116,32 @@ Args:
|
|||
", \"radius\":" + std::to_string(self.radius()) +
|
||||
", \"is_linear\":" + std::to_string(self.is_linear()) +
|
||||
", \"is_using_refiner\":" +
|
||||
std::to_string(self.is_using_refiner()) + "}";
|
||||
std::to_string(self.is_using_refiner()) +
|
||||
", \"prefetch_offset\":" +
|
||||
std::to_string(self.prefetch_offset()) +
|
||||
", \"prefetch_lines\":" +
|
||||
std::to_string(self.prefetch_lines()) + "}";
|
||||
})
|
||||
.def(py::pickle(
|
||||
[](const HnswQueryParams &self) {
|
||||
return py::make_tuple(self.ef(), self.radius(), self.is_linear(),
|
||||
self.is_using_refiner());
|
||||
self.is_using_refiner(),
|
||||
self.prefetch_offset(),
|
||||
self.prefetch_lines());
|
||||
},
|
||||
[](py::tuple t) {
|
||||
if (t.size() != 4)
|
||||
if (t.size() != 4 && t.size() != 5 && t.size() != 6)
|
||||
throw std::runtime_error("Invalid state for HnswQueryParams");
|
||||
auto obj = std::make_shared<HnswQueryParams>(t[0].cast<int>());
|
||||
obj->set_radius(t[1].cast<float>());
|
||||
obj->set_is_linear(t[2].cast<bool>());
|
||||
obj->set_is_using_refiner(t[3].cast<bool>());
|
||||
if (t.size() >= 5) {
|
||||
obj->set_prefetch_offset(t[4].cast<uint32_t>());
|
||||
}
|
||||
if (t.size() >= 6) {
|
||||
obj->set_prefetch_lines(t[5].cast<uint32_t>());
|
||||
}
|
||||
return obj;
|
||||
}));
|
||||
|
||||
|
|
@ -1298,10 +1343,24 @@ Examples:
|
|||
200
|
||||
)pbdoc");
|
||||
vamana_query_params
|
||||
.def(py::init<int, float, bool, bool>(),
|
||||
.def(py::init([](int ef_search, float radius, bool is_linear,
|
||||
bool is_using_refiner, py::dict extra_params) {
|
||||
auto obj = std::make_shared<VamanaQueryParams>(
|
||||
ef_search, radius, is_linear, is_using_refiner);
|
||||
if (extra_params.contains("prefetch_offset")) {
|
||||
obj->set_prefetch_offset(
|
||||
extra_params["prefetch_offset"].cast<uint32_t>());
|
||||
}
|
||||
if (extra_params.contains("prefetch_lines")) {
|
||||
obj->set_prefetch_lines(
|
||||
extra_params["prefetch_lines"].cast<uint32_t>());
|
||||
}
|
||||
return obj;
|
||||
}),
|
||||
py::arg("ef_search") = core_interface::kDefaultVamanaEfSearch,
|
||||
py::arg("radius") = 0.0f, py::arg("is_linear") = false,
|
||||
py::arg("is_using_refiner") = false,
|
||||
py::arg("extra_params") = py::dict(),
|
||||
R"pbdoc(
|
||||
Constructs a VamanaQueryParam instance.
|
||||
|
||||
|
|
@ -1312,11 +1371,30 @@ Args:
|
|||
is_linear (bool, optional): Force linear search. Default is False.
|
||||
is_using_refiner (bool, optional): Whether to use refiner for the query.
|
||||
Default is False.
|
||||
extra_params (dict, optional): Additional search parameters. Supported keys:
|
||||
- ``prefetch_offset`` (int): Graph prefetch offset (PO).
|
||||
``0`` disables prefetching. Default is ``8``.
|
||||
Values are clamped to ``256``.
|
||||
- ``prefetch_lines`` (int): Number of 64B cache lines to prefetch
|
||||
per neighbour vector (PL). ``0`` (default) uses the auto-derived
|
||||
value ``ceil(dim/64)``. Values are clamped to ``256``.
|
||||
)pbdoc")
|
||||
.def_property_readonly(
|
||||
"ef_search",
|
||||
[](const VamanaQueryParams &self) -> int { return self.ef_search(); },
|
||||
"int: Size of the dynamic candidate list during Vamana search.")
|
||||
.def_property_readonly(
|
||||
"prefetch_offset",
|
||||
[](const VamanaQueryParams &self) -> uint32_t {
|
||||
return self.prefetch_offset();
|
||||
},
|
||||
"int: Graph prefetch offset used by the Vamana fast path.")
|
||||
.def_property_readonly(
|
||||
"prefetch_lines",
|
||||
[](const VamanaQueryParams &self) -> uint32_t {
|
||||
return self.prefetch_lines();
|
||||
},
|
||||
"int: Override of prefetch cache lines per vector (0=auto).")
|
||||
.def("__repr__",
|
||||
[](const VamanaQueryParams &self) -> std::string {
|
||||
return "{"
|
||||
|
|
@ -1326,20 +1404,32 @@ Args:
|
|||
", \"radius\":" + std::to_string(self.radius()) +
|
||||
", \"is_linear\":" + std::to_string(self.is_linear()) +
|
||||
", \"is_using_refiner\":" +
|
||||
std::to_string(self.is_using_refiner()) + "}";
|
||||
std::to_string(self.is_using_refiner()) +
|
||||
", \"prefetch_offset\":" +
|
||||
std::to_string(self.prefetch_offset()) +
|
||||
", \"prefetch_lines\":" +
|
||||
std::to_string(self.prefetch_lines()) + "}";
|
||||
})
|
||||
.def(py::pickle(
|
||||
[](const VamanaQueryParams &self) {
|
||||
return py::make_tuple(self.ef_search(), self.radius(),
|
||||
self.is_linear(), self.is_using_refiner());
|
||||
self.is_linear(), self.is_using_refiner(),
|
||||
self.prefetch_offset(),
|
||||
self.prefetch_lines());
|
||||
},
|
||||
[](py::tuple t) {
|
||||
if (t.size() != 4)
|
||||
if (t.size() != 4 && t.size() != 5 && t.size() != 6)
|
||||
throw std::runtime_error("Invalid state for VamanaQueryParams");
|
||||
auto obj = std::make_shared<VamanaQueryParams>(t[0].cast<int>());
|
||||
obj->set_radius(t[1].cast<float>());
|
||||
obj->set_is_linear(t[2].cast<bool>());
|
||||
obj->set_is_using_refiner(t[3].cast<bool>());
|
||||
if (t.size() >= 5) {
|
||||
obj->set_prefetch_offset(t[4].cast<uint32_t>());
|
||||
}
|
||||
if (t.size() >= 6) {
|
||||
obj->set_prefetch_lines(t[5].cast<uint32_t>());
|
||||
}
|
||||
return obj;
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -191,7 +191,8 @@ template <typename EntityType, typename HeapType>
|
|||
void fast_search_neighbors(const EntityType &entity, HeapType &pool,
|
||||
VisitFilter &visit, HnswDistCalculator &dc,
|
||||
uint32_t topk, uint32_t ef, node_id_t entry_point,
|
||||
dist_t entry_dist, uint32_t prefetch_lines) {
|
||||
dist_t entry_dist, uint32_t prefetch_lines,
|
||||
uint32_t prefetch_offset) {
|
||||
const uint32_t max_deg = entity.max_degree(0); // level 0 only
|
||||
const uint32_t cap = std::max(topk, ef);
|
||||
pool.reset(static_cast<int32_t>(cap), static_cast<int32_t>(max_deg));
|
||||
|
|
@ -200,8 +201,6 @@ void fast_search_neighbors(const EntityType &entity, HeapType &pool,
|
|||
visit.set_visited(entry_point);
|
||||
pool.push_block(&entry_dist, &entry_point, 1);
|
||||
|
||||
static constexpr uint32_t GRAPH_PO = 8;
|
||||
|
||||
uint32_t buf_capacity = max_deg;
|
||||
std::vector<node_id_t> neighbor_ids(buf_capacity);
|
||||
std::vector<float> dists(buf_capacity);
|
||||
|
|
@ -221,7 +220,7 @@ void fast_search_neighbors(const EntityType &entity, HeapType &pool,
|
|||
}
|
||||
|
||||
const uint32_t po =
|
||||
std::min(static_cast<uint32_t>(neighbors.size()), GRAPH_PO);
|
||||
std::min(static_cast<uint32_t>(neighbors.size()), prefetch_offset);
|
||||
uint32_t unvisited_count = 0;
|
||||
uint32_t i = 0;
|
||||
|
||||
|
|
@ -270,8 +269,9 @@ void dual_heap_search_neighbors(const EntityType &entity, level_t level,
|
|||
node_id_t *entry_point, dist_t *dist,
|
||||
TopkHeap &topk, HnswContext *ctx,
|
||||
HnswDistCalculator &dc, FilterFn &&filter) {
|
||||
static constexpr uint32_t BATCH_SIZE = 12;
|
||||
static constexpr uint32_t PREFETCH_STEP = 2;
|
||||
const uint32_t prefetch_offset = ctx->po();
|
||||
const uint32_t prefetch_lines =
|
||||
ctx->pl() > 0 ? ctx->pl() : (entity.vector_size() + 63) / 64;
|
||||
|
||||
uint32_t buf_capacity = entity.max_degree(level);
|
||||
std::vector<node_id_t> neighbor_ids(buf_capacity);
|
||||
|
|
@ -342,8 +342,12 @@ void dual_heap_search_neighbors(const EntityType &entity, level_t level,
|
|||
}
|
||||
|
||||
// do prefetch
|
||||
for (uint32_t i = 0; i < std::min(BATCH_SIZE * PREFETCH_STEP, size); ++i) {
|
||||
ailego_prefetch(neighbor_vec_blocks[i].data());
|
||||
for (uint32_t i = 0; i < std::min(prefetch_offset, size); ++i) {
|
||||
const char *base =
|
||||
static_cast<const char *>(neighbor_vec_blocks[i].data());
|
||||
for (uint32_t cl = 0; cl < prefetch_lines; ++cl) {
|
||||
ailego_prefetch(base + cl * 64);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
|
|
@ -388,8 +392,6 @@ void HnswAlgorithm<EntityType>::search_neighbors(level_t level,
|
|||
const auto &entity = static_cast<const EntityType &>(ctx->get_entity());
|
||||
HnswDistCalculator &dc = ctx->dist_calculator();
|
||||
|
||||
const uint32_t prefetch_lines = (entity.vector_size() + 63) / 64;
|
||||
|
||||
if (!use_pool || ctx->filter().is_valid() || level != 0) {
|
||||
// Dual-heap path: add_node, filtered search, or upper-level scan.
|
||||
auto run_with_filter = [&](auto &&filter) {
|
||||
|
|
@ -410,6 +412,9 @@ void HnswAlgorithm<EntityType>::search_neighbors(level_t level,
|
|||
} else {
|
||||
// Pool-based path for level-0 unfiltered search.
|
||||
if constexpr (std::is_same_v<MemBlockType, MmapMemoryBlock>) {
|
||||
const uint32_t prefetch_lines =
|
||||
ctx->pl() > 0 ? ctx->pl() : (entity.vector_size() + 63) / 64;
|
||||
|
||||
// Fast path: direct pointer access via get_vector_ptr.
|
||||
// BlockHeap (AVX2) or LinearPool (scalar) for top-k tracking.
|
||||
const uint32_t topk_v = static_cast<uint32_t>(ctx->topk());
|
||||
|
|
@ -422,12 +427,12 @@ void HnswAlgorithm<EntityType>::search_neighbors(level_t level,
|
|||
if (avx2_ok) {
|
||||
auto &bpool = ctx->block_pool();
|
||||
fast_search_neighbors(entity, bpool, visit, dc, topk_v, ef_v,
|
||||
*entry_point, *dist, prefetch_lines);
|
||||
*entry_point, *dist, prefetch_lines, ctx->po());
|
||||
copy_pool_to_topk(bpool, topk);
|
||||
} else {
|
||||
auto &lpool = ctx->pool();
|
||||
fast_search_neighbors(entity, lpool, visit, dc, topk_v, ef_v,
|
||||
*entry_point, *dist, prefetch_lines);
|
||||
*entry_point, *dist, prefetch_lines, ctx->po());
|
||||
copy_pool_to_topk(lpool, topk);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -151,6 +151,14 @@ int HnswContext::update(const ailego::Params ¶ms) {
|
|||
topk_heap_.limit(std::max(topk_, ef_));
|
||||
}
|
||||
|
||||
if (params.has(PARAM_HNSW_SEARCHER_PO)) {
|
||||
params.get(PARAM_HNSW_SEARCHER_PO, &po_);
|
||||
}
|
||||
|
||||
if (params.has(PARAM_HNSW_SEARCHER_PL)) {
|
||||
params.get(PARAM_HNSW_SEARCHER_PL, &pl_);
|
||||
}
|
||||
|
||||
if (params.has(PARAM_HNSW_SEARCHER_MAX_SCAN_RATIO)) {
|
||||
params.get(PARAM_HNSW_SEARCHER_MAX_SCAN_RATIO, &max_scan_ratio_);
|
||||
max_scan_num_ =
|
||||
|
|
@ -171,6 +179,8 @@ int HnswContext::update(const ailego::Params ¶ms) {
|
|||
topk_heap_.limit(std::max(topk_, ef_));
|
||||
}
|
||||
params.get(PARAM_HNSW_STREAMER_EF, &ef_);
|
||||
params.get(PARAM_HNSW_STREAMER_PO, &po_);
|
||||
params.get(PARAM_HNSW_STREAMER_PL, &pl_);
|
||||
params.get(PARAM_HNSW_STREAMER_MAX_SCAN_RATIO, &max_scan_ratio_);
|
||||
params.get(PARAM_HNSW_STREAMER_MAX_SCAN_LIMIT, &max_scan_limit_);
|
||||
params.get(PARAM_HNSW_STREAMER_MIN_SCAN_LIMIT, &min_scan_limit_);
|
||||
|
|
|
|||
|
|
@ -314,6 +314,22 @@ class HnswContext : public IndexContext {
|
|||
return ef_;
|
||||
}
|
||||
|
||||
inline void set_po(uint32_t v) {
|
||||
po_ = v;
|
||||
}
|
||||
|
||||
inline uint32_t po(void) const {
|
||||
return po_;
|
||||
}
|
||||
|
||||
inline void set_pl(uint32_t v) {
|
||||
pl_ = v;
|
||||
}
|
||||
|
||||
inline uint32_t pl(void) const {
|
||||
return pl_;
|
||||
}
|
||||
|
||||
inline void set_filter_mode(uint32_t v) {
|
||||
filter_mode_ = v;
|
||||
}
|
||||
|
|
@ -524,6 +540,8 @@ class HnswContext : public IndexContext {
|
|||
uint32_t filter_mode_{VisitFilter::ByteMap};
|
||||
float negative_probability_{HnswEntity::kDefaultBFNegativeProbability};
|
||||
uint32_t ef_{HnswEntity::kDefaultEf};
|
||||
uint32_t po_{8};
|
||||
uint32_t pl_{0};
|
||||
float max_scan_ratio_{HnswEntity::kDefaultScanRatio};
|
||||
uint32_t magic_{0U};
|
||||
std::vector<IndexDocumentList> results_{};
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ static const std::string PARAM_HNSW_BUILDER_L0_MAX_NEIGHBOR_COUNT_MULTIPLIER(
|
|||
"proxima.hnsw.builder.l0_max_neighbor_count_multiplier");
|
||||
|
||||
static const std::string PARAM_HNSW_SEARCHER_EF("proxima.hnsw.searcher.ef");
|
||||
static const std::string PARAM_HNSW_SEARCHER_PO("proxima.hnsw.searcher.po");
|
||||
static const std::string PARAM_HNSW_SEARCHER_PL("proxima.hnsw.searcher.pl");
|
||||
static const std::string PARAM_HNSW_SEARCHER_BRUTE_FORCE_THRESHOLD(
|
||||
"proxima.hnsw.searcher.brute_force_threshold");
|
||||
static const std::string PARAM_HNSW_SEARCHER_NEIGHBORS_IN_MEMORY_ENABLE(
|
||||
|
|
@ -60,6 +62,8 @@ static const std::string PARAM_HNSW_STREAMER_MIN_SCAN_LIMIT(
|
|||
static const std::string PARAM_HNSW_STREAMER_MAX_SCAN_LIMIT(
|
||||
"proxima.hnsw.streamer.max_scan_limit");
|
||||
static const std::string PARAM_HNSW_STREAMER_EF("proxima.hnsw.streamer.ef");
|
||||
static const std::string PARAM_HNSW_STREAMER_PO("proxima.hnsw.streamer.po");
|
||||
static const std::string PARAM_HNSW_STREAMER_PL("proxima.hnsw.streamer.pl");
|
||||
static const std::string PARAM_HNSW_STREAMER_EFCONSTRUCTION(
|
||||
"proxima.hnsw.streamer.efconstruction");
|
||||
static const std::string PARAM_HNSW_STREAMER_MAX_NEIGHBOR_COUNT(
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ int HnswStreamer::init(const IndexMeta &imeta, const ailego::Params ¶ms) {
|
|||
|
||||
params.get(PARAM_HNSW_STREAMER_DOCS_HARD_LIMIT, &docs_hard_limit_);
|
||||
params.get(PARAM_HNSW_STREAMER_EF, &ef_);
|
||||
params.get(PARAM_HNSW_STREAMER_PO, &po_);
|
||||
params.get(PARAM_HNSW_STREAMER_PL, &pl_);
|
||||
params.get(PARAM_HNSW_STREAMER_EFCONSTRUCTION, &ef_construction_);
|
||||
params.get(PARAM_HNSW_STREAMER_VISIT_BLOOMFILTER_ENABLE, &bf_enabled_);
|
||||
params.get(PARAM_HNSW_STREAMER_VISIT_BLOOMFILTER_NEGATIVE_PROB,
|
||||
|
|
@ -417,6 +419,8 @@ IndexStreamer::Context::Pointer HnswStreamer::create_context(void) const {
|
|||
return Context::Pointer();
|
||||
}
|
||||
ctx->set_ef(ef_);
|
||||
ctx->set_po(po_);
|
||||
ctx->set_pl(pl_);
|
||||
ctx->set_max_scan_limit(max_scan_limit_);
|
||||
ctx->set_min_scan_limit(min_scan_limit_);
|
||||
ctx->set_max_scan_ratio(max_scan_ratio_);
|
||||
|
|
|
|||
|
|
@ -215,6 +215,8 @@ class HnswStreamer : public IndexStreamer {
|
|||
uint32_t upper_max_neighbor_cnt_{HnswEntity::kDefaultUpperMaxNeighborCnt};
|
||||
uint32_t l0_max_neighbor_cnt_{HnswEntity::kDefaultL0MaxNeighborCnt};
|
||||
uint32_t ef_{HnswEntity::kDefaultEf};
|
||||
uint32_t po_{8};
|
||||
uint32_t pl_{0};
|
||||
uint32_t ef_construction_{HnswEntity::kDefaultEfConstruction};
|
||||
uint32_t scaling_factor_{HnswEntity::kDefaultScalingFactor};
|
||||
size_t bruteforce_threshold_{HnswEntity::kDefaultBruteForceThreshold};
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ template <typename EntityType, typename HeapType>
|
|||
void fast_greedy_search(const EntityType &entity, HeapType &pool,
|
||||
VisitFilter &visit, VamanaDistCalculator &dc,
|
||||
uint32_t topk, uint32_t ef, node_id_t entry_point,
|
||||
uint32_t prefetch_lines) {
|
||||
uint32_t prefetch_lines, uint32_t prefetch_offset) {
|
||||
const uint32_t max_deg = entity.max_degree();
|
||||
const uint32_t cap = std::max(topk, ef);
|
||||
pool.reset(static_cast<int32_t>(cap), static_cast<int32_t>(max_deg));
|
||||
|
|
@ -136,8 +136,6 @@ void fast_greedy_search(const EntityType &entity, HeapType &pool,
|
|||
visit.set_visited(entry_point);
|
||||
pool.push_block(&ep_dist, &entry_point, 1);
|
||||
|
||||
static constexpr uint32_t GRAPH_PO = 8;
|
||||
|
||||
uint32_t buf_capacity = max_deg;
|
||||
std::vector<node_id_t> neighbor_ids(buf_capacity);
|
||||
std::vector<float> dists(buf_capacity);
|
||||
|
|
@ -157,7 +155,7 @@ void fast_greedy_search(const EntityType &entity, HeapType &pool,
|
|||
}
|
||||
|
||||
const uint32_t po =
|
||||
std::min(static_cast<uint32_t>(neighbors.size()), GRAPH_PO);
|
||||
std::min(static_cast<uint32_t>(neighbors.size()), prefetch_offset);
|
||||
uint32_t unvisited_count = 0;
|
||||
uint32_t i = 0;
|
||||
|
||||
|
|
@ -200,8 +198,9 @@ template <typename EntityType, typename MemBlockType, typename FilterFn>
|
|||
void dual_heap_greedy_search(const EntityType &entity, VamanaContext *ctx,
|
||||
VamanaDistCalculator &dc, node_id_t entry_point,
|
||||
FilterFn &&filter) {
|
||||
static constexpr uint32_t PREFETCH_BATCH = 2;
|
||||
static constexpr uint32_t PREFETCH_STEP = 2;
|
||||
const uint32_t prefetch_offset = ctx->po();
|
||||
const uint32_t prefetch_lines =
|
||||
ctx->pl() > 0 ? ctx->pl() : (entity.vector_size() + 63) / 64;
|
||||
|
||||
uint32_t buf_capacity = entity.max_degree();
|
||||
std::vector<node_id_t> neighbor_ids(buf_capacity);
|
||||
|
|
@ -273,9 +272,12 @@ void dual_heap_greedy_search(const EntityType &entity, VamanaContext *ctx,
|
|||
neighbor_vec_blocks);
|
||||
if (ailego_unlikely(ret != 0)) break;
|
||||
|
||||
for (uint32_t i = 0;
|
||||
i < std::min(PREFETCH_BATCH * PREFETCH_STEP, unvisited_count); ++i) {
|
||||
ailego_prefetch(neighbor_vec_blocks[i].data());
|
||||
for (uint32_t i = 0; i < std::min(prefetch_offset, unvisited_count); ++i) {
|
||||
const char *base =
|
||||
static_cast<const char *>(neighbor_vec_blocks[i].data());
|
||||
for (uint32_t cl = 0; cl < prefetch_lines; ++cl) {
|
||||
ailego_prefetch(base + cl * 64);
|
||||
}
|
||||
}
|
||||
|
||||
// Batch distance computation (reuse pre-allocated buffers).
|
||||
|
|
@ -316,17 +318,8 @@ void VamanaAlgorithm<EntityType>::greedy_search(node_id_t entry_point,
|
|||
const IndexFilter &index_filter =
|
||||
static_cast<const IndexContext *>(ctx)->filter();
|
||||
|
||||
// Number of cache lines per vector (e.g. 2 for dim=128).
|
||||
// Used by both the fallback candidates/filter path and the fast helpers.
|
||||
uint32_t prefetch_lines = (dc.dimension() + 63) / 64;
|
||||
if constexpr (std::is_same_v<EntityType, VamanaContiguousStreamerEntity>) {
|
||||
// Contiguous flat array stride is already 64B-aligned. Use it so that
|
||||
// prefetch does not overshoot into the next vector.
|
||||
size_t stride = entity.vector_stride();
|
||||
if (stride > 0) {
|
||||
prefetch_lines = static_cast<uint32_t>(stride / 64);
|
||||
}
|
||||
}
|
||||
const uint32_t prefetch_lines =
|
||||
ctx->pl() > 0 ? ctx->pl() : (entity.vector_size() + 63) / 64;
|
||||
|
||||
if (!use_pool || index_filter.is_valid()) {
|
||||
// Fallback path used by add_node (use_pool=false) and filtered search.
|
||||
|
|
@ -362,12 +355,12 @@ void VamanaAlgorithm<EntityType>::greedy_search(node_id_t entry_point,
|
|||
if (avx2_ok) {
|
||||
auto &bpool = ctx->block_pool();
|
||||
fast_greedy_search(entity, bpool, visit, dc, topk_v, ef_v, entry_point,
|
||||
prefetch_lines);
|
||||
prefetch_lines, ctx->po());
|
||||
copy_pool_to_topk(bpool, topk_heap);
|
||||
} else {
|
||||
auto &lpool = ctx->pool();
|
||||
fast_greedy_search(entity, lpool, visit, dc, topk_v, ef_v, entry_point,
|
||||
prefetch_lines);
|
||||
prefetch_lines, ctx->po());
|
||||
copy_pool_to_topk(lpool, topk_heap);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -119,6 +119,12 @@ int VamanaContext::update(const ailego::Params ¶ms) {
|
|||
params.get(PARAM_VAMANA_STREAMER_EF, &ef);
|
||||
ef_ = ef;
|
||||
topk_heap_.limit(std::max(topk_, ef_));
|
||||
uint32_t po = po_;
|
||||
params.get(PARAM_VAMANA_STREAMER_PO, &po);
|
||||
po_ = po;
|
||||
uint32_t pl = pl_;
|
||||
params.get(PARAM_VAMANA_STREAMER_PL, &pl);
|
||||
pl_ = pl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,6 +177,21 @@ class VamanaContext : public IndexContext {
|
|||
inline uint32_t ef() const {
|
||||
return ef_;
|
||||
}
|
||||
inline void set_po(uint32_t v) {
|
||||
po_ = v;
|
||||
}
|
||||
|
||||
inline uint32_t po() const {
|
||||
return po_;
|
||||
}
|
||||
|
||||
inline void set_pl(uint32_t v) {
|
||||
pl_ = v;
|
||||
}
|
||||
|
||||
inline uint32_t pl() const {
|
||||
return pl_;
|
||||
}
|
||||
inline void set_max_scan_ratio(float v) {
|
||||
max_scan_ratio_ = v;
|
||||
}
|
||||
|
|
@ -296,6 +311,8 @@ class VamanaContext : public IndexContext {
|
|||
uint32_t reserve_max_doc_cnt_{kMinReserveDocCnt};
|
||||
uint32_t topk_{0};
|
||||
uint32_t ef_{VamanaEntity::kDefaultEf};
|
||||
uint32_t po_{8};
|
||||
uint32_t pl_{0};
|
||||
float max_scan_ratio_{VamanaEntity::kDefaultScanRatio};
|
||||
size_t max_scan_limit_{VamanaEntity::kDefaultMaxScanLimit};
|
||||
size_t min_scan_limit_{VamanaEntity::kDefaultMinScanLimit};
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ static const std::string PARAM_VAMANA_STREAMER_ALPHA(
|
|||
static const std::string PARAM_VAMANA_STREAMER_MAX_OCCLUSION_SIZE(
|
||||
"proxima.vamana.streamer.max_occlusion_size");
|
||||
static const std::string PARAM_VAMANA_STREAMER_EF("proxima.vamana.streamer.ef");
|
||||
static const std::string PARAM_VAMANA_STREAMER_PO("proxima.vamana.streamer.po");
|
||||
static const std::string PARAM_VAMANA_STREAMER_PL("proxima.vamana.streamer.pl");
|
||||
static const std::string PARAM_VAMANA_STREAMER_BRUTE_FORCE_THRESHOLD(
|
||||
"proxima.vamana.streamer.brute_force_threshold");
|
||||
static const std::string PARAM_VAMANA_STREAMER_MAX_SCAN_RATIO(
|
||||
|
|
|
|||
|
|
@ -166,6 +166,12 @@ std::string IndexFactory::QueryParamSerializeToJson(const QueryParamType ¶m,
|
|||
if (!omit_empty_value || param.ef_search != 0) {
|
||||
json_obj.set("ef_search", ailego::JsonValue(param.ef_search));
|
||||
}
|
||||
if (!omit_empty_value || param.prefetch_offset != 0) {
|
||||
json_obj.set("prefetch_offset", ailego::JsonValue(param.prefetch_offset));
|
||||
}
|
||||
if (!omit_empty_value || param.prefetch_lines != 0) {
|
||||
json_obj.set("prefetch_lines", ailego::JsonValue(param.prefetch_lines));
|
||||
}
|
||||
index_type = IndexType::kHNSW;
|
||||
} else if constexpr (std::is_same_v<QueryParamType, IVFQueryParam>) {
|
||||
if (!omit_empty_value || param.nprobe != 0) {
|
||||
|
|
@ -185,6 +191,12 @@ std::string IndexFactory::QueryParamSerializeToJson(const QueryParamType ¶m,
|
|||
if (!omit_empty_value || param.ef_search != 0) {
|
||||
json_obj.set("ef_search", ailego::JsonValue(param.ef_search));
|
||||
}
|
||||
if (!omit_empty_value || param.prefetch_offset != 0) {
|
||||
json_obj.set("prefetch_offset", ailego::JsonValue(param.prefetch_offset));
|
||||
}
|
||||
if (!omit_empty_value || param.prefetch_lines != 0) {
|
||||
json_obj.set("prefetch_lines", ailego::JsonValue(param.prefetch_lines));
|
||||
}
|
||||
index_type = IndexType::kVamana;
|
||||
}
|
||||
|
||||
|
|
@ -268,6 +280,16 @@ typename QueryParamType::Pointer IndexFactory::QueryParamDeserializeFromJson(
|
|||
LOG_ERROR("Failed to deserialize ef_search");
|
||||
return nullptr;
|
||||
}
|
||||
if (!extract_value_from_json(json_obj, "prefetch_offset",
|
||||
param->prefetch_offset, tmp_json_value)) {
|
||||
LOG_ERROR("Failed to deserialize prefetch_offset");
|
||||
return nullptr;
|
||||
}
|
||||
if (!extract_value_from_json(json_obj, "prefetch_lines",
|
||||
param->prefetch_lines, tmp_json_value)) {
|
||||
LOG_ERROR("Failed to deserialize prefetch_lines");
|
||||
return nullptr;
|
||||
}
|
||||
return param;
|
||||
} else if (index_type == IndexType::kIVF) {
|
||||
auto param = std::make_shared<IVFQueryParam>();
|
||||
|
|
@ -301,6 +323,16 @@ typename QueryParamType::Pointer IndexFactory::QueryParamDeserializeFromJson(
|
|||
LOG_ERROR("Failed to deserialize ef_search");
|
||||
return nullptr;
|
||||
}
|
||||
if (!extract_value_from_json(json_obj, "prefetch_offset",
|
||||
param->prefetch_offset, tmp_json_value)) {
|
||||
LOG_ERROR("Failed to deserialize prefetch_offset");
|
||||
return nullptr;
|
||||
}
|
||||
if (!extract_value_from_json(json_obj, "prefetch_lines",
|
||||
param->prefetch_lines, tmp_json_value)) {
|
||||
LOG_ERROR("Failed to deserialize prefetch_lines");
|
||||
return nullptr;
|
||||
}
|
||||
return param;
|
||||
} else {
|
||||
LOG_ERROR("Unsupported index type: %s",
|
||||
|
|
@ -319,6 +351,16 @@ typename QueryParamType::Pointer IndexFactory::QueryParamDeserializeFromJson(
|
|||
LOG_ERROR("Failed to deserialize ef_search");
|
||||
return nullptr;
|
||||
}
|
||||
if (!extract_value_from_json(json_obj, "prefetch_offset",
|
||||
param->prefetch_offset, tmp_json_value)) {
|
||||
LOG_ERROR("Failed to deserialize prefetch_offset");
|
||||
return nullptr;
|
||||
}
|
||||
if (!extract_value_from_json(json_obj, "prefetch_lines",
|
||||
param->prefetch_lines, tmp_json_value)) {
|
||||
LOG_ERROR("Failed to deserialize prefetch_lines");
|
||||
return nullptr;
|
||||
}
|
||||
} else if constexpr (std::is_same_v<QueryParamType, IVFQueryParam>) {
|
||||
if (!extract_value_from_json(json_obj, "nprobe", param->nprobe,
|
||||
tmp_json_value)) {
|
||||
|
|
@ -337,6 +379,16 @@ typename QueryParamType::Pointer IndexFactory::QueryParamDeserializeFromJson(
|
|||
LOG_ERROR("Failed to deserialize ef_search");
|
||||
return nullptr;
|
||||
}
|
||||
if (!extract_value_from_json(json_obj, "prefetch_offset",
|
||||
param->prefetch_offset, tmp_json_value)) {
|
||||
LOG_ERROR("Failed to deserialize prefetch_offset");
|
||||
return nullptr;
|
||||
}
|
||||
if (!extract_value_from_json(json_obj, "prefetch_lines",
|
||||
param->prefetch_lines, tmp_json_value)) {
|
||||
LOG_ERROR("Failed to deserialize prefetch_lines");
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Unsupported index type: %s",
|
||||
magic_enum::enum_name(index_type).data());
|
||||
|
|
|
|||
|
|
@ -128,6 +128,12 @@ int HNSWIndex::_prepare_for_search(
|
|||
const int real_search_ef =
|
||||
std::max(1u, std::min(2048u, hnsw_search_param->ef_search));
|
||||
params.set(core::PARAM_HNSW_STREAMER_EF, real_search_ef);
|
||||
const uint32_t real_search_po =
|
||||
std::min(256u, hnsw_search_param->prefetch_offset);
|
||||
params.set(core::PARAM_HNSW_STREAMER_PO, real_search_po);
|
||||
const uint32_t real_search_pl =
|
||||
std::min(256u, hnsw_search_param->prefetch_lines);
|
||||
params.set(core::PARAM_HNSW_STREAMER_PL, real_search_pl);
|
||||
context->update(params);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,12 @@ int VamanaIndex::_prepare_for_search(
|
|||
const uint32_t real_search_ef =
|
||||
std::max(1u, std::min(2048u, vamana_search_param->ef_search));
|
||||
params.set(core::PARAM_VAMANA_STREAMER_EF, real_search_ef);
|
||||
const uint32_t real_search_po =
|
||||
std::min(256u, vamana_search_param->prefetch_offset);
|
||||
params.set(core::PARAM_VAMANA_STREAMER_PO, real_search_po);
|
||||
const uint32_t real_search_pl =
|
||||
std::min(256u, vamana_search_param->prefetch_lines);
|
||||
params.set(core::PARAM_VAMANA_STREAMER_PL, real_search_pl);
|
||||
context->update(params);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,10 @@ class ProximaEngineHelper {
|
|||
auto db_hnsw_query_params = dynamic_cast<const HnswQueryParams *>(
|
||||
query_params.query_params.get());
|
||||
hnsw_query_param->ef_search = db_hnsw_query_params->ef();
|
||||
hnsw_query_param->prefetch_offset =
|
||||
db_hnsw_query_params->prefetch_offset();
|
||||
hnsw_query_param->prefetch_lines =
|
||||
db_hnsw_query_params->prefetch_lines();
|
||||
}
|
||||
return std::move(hnsw_query_param);
|
||||
}
|
||||
|
|
@ -238,6 +242,10 @@ class ProximaEngineHelper {
|
|||
query_params.query_params.get());
|
||||
vamana_query_param->ef_search =
|
||||
static_cast<uint32_t>(db_vamana_query_params->ef_search());
|
||||
vamana_query_param->prefetch_offset =
|
||||
db_vamana_query_params->prefetch_offset();
|
||||
vamana_query_param->prefetch_lines =
|
||||
db_vamana_query_params->prefetch_lines();
|
||||
}
|
||||
return std::move(vamana_query_param);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -826,6 +826,7 @@ typedef uint32_t zvec_index_type_t;
|
|||
#define ZVEC_INDEX_TYPE_HNSW 1
|
||||
#define ZVEC_INDEX_TYPE_IVF 2
|
||||
#define ZVEC_INDEX_TYPE_FLAT 3
|
||||
#define ZVEC_INDEX_TYPE_VAMANA 6
|
||||
#define ZVEC_INDEX_TYPE_INVERT 10
|
||||
#define ZVEC_INDEX_TYPE_FTS 11
|
||||
|
||||
|
|
@ -986,6 +987,37 @@ zvec_index_params_get_hnsw_m(const zvec_index_params_t *params);
|
|||
ZVEC_EXPORT int ZVEC_CALL
|
||||
zvec_index_params_get_hnsw_ef_construction(const zvec_index_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Set Vamana specific parameters
|
||||
* @param params Index parameters (must be VAMANA type)
|
||||
* @param max_degree Maximum out-degree (R) of every node (default: 64)
|
||||
* @param search_list_size Candidate list size during construction (default:
|
||||
* 100)
|
||||
* @param alpha RobustPrune alpha factor (default: 1.2)
|
||||
* @param saturate_graph Force every node to reach max_degree (default: false)
|
||||
* @param use_contiguous_memory Allocate contiguous memory arena (default:
|
||||
* false)
|
||||
* @return ZVEC_OK on success, error code on failure
|
||||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_index_params_set_vamana_params(
|
||||
zvec_index_params_t *params, int max_degree, int search_list_size,
|
||||
float alpha, bool saturate_graph, bool use_contiguous_memory);
|
||||
|
||||
/**
|
||||
* @brief Get Vamana parameters (all at once)
|
||||
* @param params Index parameters (must be VAMANA type)
|
||||
* @param[out] out_max_degree Maximum out-degree
|
||||
* @param[out] out_search_list_size Construction candidate list size
|
||||
* @param[out] out_alpha RobustPrune alpha factor
|
||||
* @param[out] out_saturate_graph Whether saturate graph is enabled
|
||||
* @param[out] out_use_contiguous_memory Whether contiguous memory is enabled
|
||||
* @return ZVEC_OK on success, error code on failure
|
||||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_index_params_get_vamana_params(
|
||||
const zvec_index_params_t *params, int *out_max_degree,
|
||||
int *out_search_list_size, float *out_alpha, bool *out_saturate_graph,
|
||||
bool *out_use_contiguous_memory);
|
||||
|
||||
/**
|
||||
* @brief Set IVF specific parameters
|
||||
* @param params Index parameters (must be IVF type)
|
||||
|
|
@ -1102,6 +1134,16 @@ typedef struct zvec_flat_query_params_t zvec_flat_query_params_t;
|
|||
*/
|
||||
typedef struct zvec_fts_query_params_t zvec_fts_query_params_t;
|
||||
|
||||
/**
|
||||
* @brief Vamana query parameters handle (opaque pointer)
|
||||
*
|
||||
* Internally maps to zvec::VamanaQueryParams* (raw pointer).
|
||||
* Created by zvec_query_params_vamana_create() and destroyed by
|
||||
* zvec_query_params_vamana_destroy(). Caller owns the pointer and must
|
||||
* explicitly destroy it.
|
||||
*/
|
||||
typedef struct zvec_vamana_query_params_t zvec_vamana_query_params_t;
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// Query Structures (Opaque Pointer Pattern)
|
||||
|
|
@ -1490,6 +1532,99 @@ zvec_query_params_fts_set_default_operator(zvec_fts_query_params_t *params,
|
|||
ZVEC_EXPORT const char *ZVEC_CALL zvec_query_params_fts_get_default_operator(
|
||||
const zvec_fts_query_params_t *params);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// zvec_vamana_query_params_t (Vamana Query Parameters)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Create Vamana query parameters
|
||||
* @param ef_search Search-time candidate list size (default: 200)
|
||||
* @param radius Search radius (default: 0.0)
|
||||
* @param is_linear Whether linear search (default: false)
|
||||
* @param is_using_refiner Whether using refiner (default: false)
|
||||
* @return zvec_vamana_query_params_t* Pointer to the newly created Vamana
|
||||
* query parameters
|
||||
*/
|
||||
ZVEC_EXPORT zvec_vamana_query_params_t *ZVEC_CALL
|
||||
zvec_query_params_vamana_create(int ef_search, float radius, bool is_linear,
|
||||
bool is_using_refiner);
|
||||
|
||||
/**
|
||||
* @brief Destroy Vamana query parameters
|
||||
* @param params Vamana query parameters pointer
|
||||
*/
|
||||
ZVEC_EXPORT void ZVEC_CALL
|
||||
zvec_query_params_vamana_destroy(zvec_vamana_query_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Set search-time candidate list size
|
||||
* @param params Vamana query parameters pointer
|
||||
* @param ef_search Candidate list size
|
||||
* @return zvec_error_code_t Error code
|
||||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_query_params_vamana_set_ef_search(
|
||||
zvec_vamana_query_params_t *params, int ef_search);
|
||||
|
||||
/**
|
||||
* @brief Get search-time candidate list size
|
||||
* @param params Vamana query parameters pointer
|
||||
* @return int Candidate list size
|
||||
*/
|
||||
ZVEC_EXPORT int ZVEC_CALL zvec_query_params_vamana_get_ef_search(
|
||||
const zvec_vamana_query_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Set search radius
|
||||
* @param params Vamana query parameters pointer
|
||||
* @param radius Search radius
|
||||
* @return zvec_error_code_t Error code
|
||||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_query_params_vamana_set_radius(
|
||||
zvec_vamana_query_params_t *params, float radius);
|
||||
|
||||
/**
|
||||
* @brief Get search radius
|
||||
* @param params Vamana query parameters pointer
|
||||
* @return float Search radius
|
||||
*/
|
||||
ZVEC_EXPORT float ZVEC_CALL
|
||||
zvec_query_params_vamana_get_radius(const zvec_vamana_query_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Set linear search mode
|
||||
* @param params Vamana query parameters pointer
|
||||
* @param is_linear Whether linear search
|
||||
* @return zvec_error_code_t Error code
|
||||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_query_params_vamana_set_is_linear(
|
||||
zvec_vamana_query_params_t *params, bool is_linear);
|
||||
|
||||
/**
|
||||
* @brief Get linear search mode
|
||||
* @param params Vamana query parameters pointer
|
||||
* @return bool Whether linear search
|
||||
*/
|
||||
ZVEC_EXPORT bool ZVEC_CALL zvec_query_params_vamana_get_is_linear(
|
||||
const zvec_vamana_query_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Set whether to use refiner
|
||||
* @param params Vamana query parameters pointer
|
||||
* @param is_using_refiner Whether to use refiner
|
||||
* @return zvec_error_code_t Error code
|
||||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL
|
||||
zvec_query_params_vamana_set_is_using_refiner(
|
||||
zvec_vamana_query_params_t *params, bool is_using_refiner);
|
||||
|
||||
/**
|
||||
* @brief Get whether to use refiner
|
||||
* @param params Vamana query parameters pointer
|
||||
* @return bool Whether to use refiner
|
||||
*/
|
||||
ZVEC_EXPORT bool ZVEC_CALL zvec_query_params_vamana_get_is_using_refiner(
|
||||
const zvec_vamana_query_params_t *params);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// zvec_vector_query_t (Vector Query)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -1672,6 +1807,15 @@ ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_vector_query_set_flat_params(
|
|||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_vector_query_set_fts_params(
|
||||
zvec_vector_query_t *query, zvec_fts_query_params_t *fts_params);
|
||||
|
||||
/**
|
||||
* @brief Set Vamana query parameters for vector query
|
||||
* @param query Vector query pointer
|
||||
* @param vamana_params Vamana query parameters pointer
|
||||
* @return zvec_error_code_t Error code
|
||||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_vector_query_set_vamana_params(
|
||||
zvec_vector_query_t *query, zvec_vamana_query_params_t *vamana_params);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// zvec_fts_t (FTS query payload)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -1944,6 +2088,17 @@ ZVEC_EXPORT zvec_error_code_t ZVEC_CALL
|
|||
zvec_group_by_vector_query_set_flat_params(
|
||||
zvec_group_by_vector_query_t *query, zvec_flat_query_params_t *flat_params);
|
||||
|
||||
/**
|
||||
* @brief Set Vamana query parameters (takes ownership)
|
||||
* @param query Group by vector query pointer
|
||||
* @param vamana_params Vamana query parameters pointer
|
||||
* @return zvec_error_code_t Error code
|
||||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL
|
||||
zvec_group_by_vector_query_set_vamana_params(
|
||||
zvec_group_by_vector_query_t *query,
|
||||
zvec_vamana_query_params_t *vamana_params);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Rerank Strategy (set on MultiQuery)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -2194,6 +2349,16 @@ ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_sub_query_set_ivf_params(
|
|||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_sub_query_set_flat_params(
|
||||
zvec_sub_query_t *query, zvec_flat_query_params_t *flat_params);
|
||||
|
||||
/**
|
||||
* @brief Set Vamana query parameters (takes ownership)
|
||||
* @param query Sub-query pointer
|
||||
* @param vamana_params Vamana query parameters pointer
|
||||
* @return zvec_error_code_t Error code
|
||||
*/
|
||||
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_sub_query_set_vamana_params(
|
||||
zvec_sub_query_t *query, zvec_vamana_query_params_t *vamana_params);
|
||||
|
||||
// =============================================================================
|
||||
// Collection Options and Statistics (Opaque Pointer Pattern)
|
||||
// =============================================================================
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ constexpr static uint32_t kDefaultHnswEfConstruction = 500;
|
|||
constexpr static uint32_t kDefaultHnswNeighborCnt = 50;
|
||||
|
||||
constexpr static uint32_t kDefaultHnswEfSearch = 300;
|
||||
constexpr static uint32_t kDefaultPrefetchOffset = 8;
|
||||
constexpr static uint32_t kDefaultPrefetchLines = 0;
|
||||
|
||||
constexpr static uint32_t kDefaultVamanaMaxDegree = 64;
|
||||
constexpr static uint32_t kDefaultVamanaSearchListSize = 100;
|
||||
|
|
|
|||
|
|
@ -185,6 +185,8 @@ struct HNSWQueryParam : public BaseIndexQueryParam {
|
|||
using Pointer = std::shared_ptr<HNSWQueryParam>;
|
||||
|
||||
uint32_t ef_search = kDefaultHnswEfSearch;
|
||||
uint32_t prefetch_offset = kDefaultPrefetchOffset;
|
||||
uint32_t prefetch_lines = kDefaultPrefetchLines;
|
||||
|
||||
BaseIndexQueryParam::Pointer Clone() const override {
|
||||
return std::make_shared<HNSWQueryParam>(*this);
|
||||
|
|
@ -377,6 +379,8 @@ struct VamanaQueryParam : public BaseIndexQueryParam {
|
|||
using Pointer = std::shared_ptr<VamanaQueryParam>;
|
||||
|
||||
uint32_t ef_search = kDefaultVamanaEfSearch;
|
||||
uint32_t prefetch_offset = kDefaultPrefetchOffset;
|
||||
uint32_t prefetch_lines = kDefaultPrefetchLines;
|
||||
|
||||
BaseIndexQueryParam::Pointer Clone() const override {
|
||||
return std::make_shared<VamanaQueryParam>(*this);
|
||||
|
|
|
|||
|
|
@ -354,6 +354,16 @@ class HNSWQueryParamBuilder
|
|||
return *this;
|
||||
}
|
||||
|
||||
HNSWQueryParamBuilder &with_prefetch_offset(uint32_t prefetch_offset) {
|
||||
m_param.prefetch_offset = prefetch_offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HNSWQueryParamBuilder &with_prefetch_lines(uint32_t prefetch_lines) {
|
||||
m_param.prefetch_lines = prefetch_lines;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HNSWQueryParam::Pointer build() {
|
||||
return std::make_shared<HNSWQueryParam>(std::move(m_param));
|
||||
}
|
||||
|
|
@ -419,6 +429,16 @@ class VamanaQueryParamBuilder
|
|||
return *this;
|
||||
}
|
||||
|
||||
VamanaQueryParamBuilder &with_prefetch_offset(uint32_t prefetch_offset) {
|
||||
m_param.prefetch_offset = prefetch_offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VamanaQueryParamBuilder &with_prefetch_lines(uint32_t prefetch_lines) {
|
||||
m_param.prefetch_lines = prefetch_lines;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VamanaQueryParam::Pointer build() {
|
||||
return std::make_shared<VamanaQueryParam>(std::move(m_param));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,10 +71,15 @@ class QueryParams {
|
|||
|
||||
class HnswQueryParams : public QueryParams {
|
||||
public:
|
||||
HnswQueryParams(int ef = core_interface::kDefaultHnswEfSearch,
|
||||
float radius = 0.0f, bool is_linear = false,
|
||||
bool is_using_refiner = false)
|
||||
: QueryParams(IndexType::HNSW), ef_(ef) {
|
||||
HnswQueryParams(
|
||||
int ef = core_interface::kDefaultHnswEfSearch, float radius = 0.0f,
|
||||
bool is_linear = false, bool is_using_refiner = false,
|
||||
uint32_t prefetch_offset = core_interface::kDefaultPrefetchOffset,
|
||||
uint32_t prefetch_lines = core_interface::kDefaultPrefetchLines)
|
||||
: QueryParams(IndexType::HNSW),
|
||||
ef_(ef),
|
||||
prefetch_offset_(prefetch_offset),
|
||||
prefetch_lines_(prefetch_lines) {
|
||||
set_radius(radius);
|
||||
set_is_linear(is_linear);
|
||||
set_is_using_refiner(is_using_refiner);
|
||||
|
|
@ -90,8 +95,26 @@ class HnswQueryParams : public QueryParams {
|
|||
ef_ = ef;
|
||||
}
|
||||
|
||||
uint32_t prefetch_offset() const {
|
||||
return prefetch_offset_;
|
||||
}
|
||||
|
||||
void set_prefetch_offset(uint32_t prefetch_offset) {
|
||||
prefetch_offset_ = prefetch_offset;
|
||||
}
|
||||
|
||||
uint32_t prefetch_lines() const {
|
||||
return prefetch_lines_;
|
||||
}
|
||||
|
||||
void set_prefetch_lines(uint32_t prefetch_lines) {
|
||||
prefetch_lines_ = prefetch_lines;
|
||||
}
|
||||
|
||||
private:
|
||||
int ef_;
|
||||
uint32_t prefetch_offset_{core_interface::kDefaultPrefetchOffset};
|
||||
uint32_t prefetch_lines_{core_interface::kDefaultPrefetchLines};
|
||||
};
|
||||
|
||||
class IVFQueryParams : public QueryParams {
|
||||
|
|
@ -197,10 +220,16 @@ class DiskAnnQueryParams : public QueryParams {
|
|||
|
||||
class VamanaQueryParams : public QueryParams {
|
||||
public:
|
||||
VamanaQueryParams(int ef_search = core_interface::kDefaultVamanaEfSearch,
|
||||
float radius = 0.0f, bool is_linear = false,
|
||||
bool is_using_refiner = false)
|
||||
: QueryParams(IndexType::VAMANA), ef_search_(ef_search) {
|
||||
VamanaQueryParams(
|
||||
int ef_search = core_interface::kDefaultVamanaEfSearch,
|
||||
float radius = 0.0f, bool is_linear = false,
|
||||
bool is_using_refiner = false,
|
||||
uint32_t prefetch_offset = core_interface::kDefaultPrefetchOffset,
|
||||
uint32_t prefetch_lines = core_interface::kDefaultPrefetchLines)
|
||||
: QueryParams(IndexType::VAMANA),
|
||||
ef_search_(ef_search),
|
||||
prefetch_offset_(prefetch_offset),
|
||||
prefetch_lines_(prefetch_lines) {
|
||||
set_radius(radius);
|
||||
set_is_linear(is_linear);
|
||||
set_is_using_refiner(is_using_refiner);
|
||||
|
|
@ -216,8 +245,26 @@ class VamanaQueryParams : public QueryParams {
|
|||
ef_search_ = ef_search;
|
||||
}
|
||||
|
||||
uint32_t prefetch_offset() const {
|
||||
return prefetch_offset_;
|
||||
}
|
||||
|
||||
void set_prefetch_offset(uint32_t prefetch_offset) {
|
||||
prefetch_offset_ = prefetch_offset;
|
||||
}
|
||||
|
||||
uint32_t prefetch_lines() const {
|
||||
return prefetch_lines_;
|
||||
}
|
||||
|
||||
void set_prefetch_lines(uint32_t prefetch_lines) {
|
||||
prefetch_lines_ = prefetch_lines;
|
||||
}
|
||||
|
||||
private:
|
||||
int ef_search_;
|
||||
uint32_t prefetch_offset_{core_interface::kDefaultPrefetchOffset};
|
||||
uint32_t prefetch_lines_{core_interface::kDefaultPrefetchLines};
|
||||
};
|
||||
|
||||
class FtsQueryParams : public QueryParams {
|
||||
|
|
|
|||
|
|
@ -3695,16 +3695,46 @@ void test_query_params_functions(void) {
|
|||
is_using_refiner = zvec_query_params_flat_get_is_using_refiner(flat_params);
|
||||
TEST_ASSERT(is_using_refiner == true);
|
||||
|
||||
// Test Vamana query parameters
|
||||
zvec_vamana_query_params_t *vamana_params =
|
||||
zvec_query_params_vamana_create(256, 0.3f, false, true);
|
||||
TEST_ASSERT(vamana_params != NULL);
|
||||
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_ef_search(vamana_params) == 256);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_radius(vamana_params) == 0.3f);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_is_linear(vamana_params) == false);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_is_using_refiner(vamana_params) ==
|
||||
true);
|
||||
|
||||
// Vamana set/get all parameters
|
||||
err = zvec_query_params_vamana_set_ef_search(vamana_params, 512);
|
||||
TEST_ASSERT(err == ZVEC_OK);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_ef_search(vamana_params) == 512);
|
||||
|
||||
err = zvec_query_params_vamana_set_radius(vamana_params, 0.5f);
|
||||
TEST_ASSERT(err == ZVEC_OK);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_radius(vamana_params) == 0.5f);
|
||||
|
||||
err = zvec_query_params_vamana_set_is_linear(vamana_params, true);
|
||||
TEST_ASSERT(err == ZVEC_OK);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_is_linear(vamana_params) == true);
|
||||
|
||||
err = zvec_query_params_vamana_set_is_using_refiner(vamana_params, false);
|
||||
TEST_ASSERT(err == ZVEC_OK);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_is_using_refiner(vamana_params) ==
|
||||
false);
|
||||
|
||||
// Test destruction of valid parameters
|
||||
zvec_query_params_hnsw_destroy(hnsw_params);
|
||||
zvec_query_params_ivf_destroy(ivf_params);
|
||||
zvec_query_params_flat_destroy(flat_params);
|
||||
|
||||
zvec_query_params_vamana_destroy(vamana_params);
|
||||
|
||||
// Test boundary cases - null pointer handling
|
||||
zvec_query_params_hnsw_destroy(NULL);
|
||||
zvec_query_params_ivf_destroy(NULL);
|
||||
zvec_query_params_flat_destroy(NULL);
|
||||
zvec_query_params_vamana_destroy(NULL);
|
||||
|
||||
// Test null pointer handling for setters
|
||||
err = zvec_query_params_hnsw_set_radius(NULL, 0.5f);
|
||||
|
|
@ -3713,6 +3743,8 @@ void test_query_params_functions(void) {
|
|||
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
|
||||
err = zvec_query_params_flat_set_radius(NULL, 0.5f);
|
||||
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
|
||||
err = zvec_query_params_vamana_set_ef_search(NULL, 100);
|
||||
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
// Test default values for getters with NULL
|
||||
TEST_ASSERT(zvec_query_params_hnsw_get_radius(NULL) == 0.0f);
|
||||
|
|
@ -3724,6 +3756,10 @@ void test_query_params_functions(void) {
|
|||
TEST_ASSERT(zvec_query_params_hnsw_get_is_using_refiner(NULL) == false);
|
||||
TEST_ASSERT(zvec_query_params_ivf_get_is_using_refiner(NULL) == false);
|
||||
TEST_ASSERT(zvec_query_params_flat_get_is_using_refiner(NULL) == false);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_ef_search(NULL) == 200);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_radius(NULL) == 0.0f);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_is_linear(NULL) == false);
|
||||
TEST_ASSERT(zvec_query_params_vamana_get_is_using_refiner(NULL) == false);
|
||||
|
||||
TEST_END();
|
||||
}
|
||||
|
|
@ -4972,11 +5008,50 @@ void test_index_params_creation_functions(void) {
|
|||
TEST_ASSERT(enable_range_opt == true);
|
||||
TEST_ASSERT(enable_wildcard == false);
|
||||
|
||||
// Test Vamana parameters using new API
|
||||
zvec_index_params_t *vamana_params =
|
||||
zvec_index_params_create(ZVEC_INDEX_TYPE_VAMANA);
|
||||
TEST_ASSERT(vamana_params != NULL);
|
||||
TEST_ASSERT(zvec_index_params_get_type(vamana_params) ==
|
||||
ZVEC_INDEX_TYPE_VAMANA);
|
||||
TEST_ASSERT(zvec_index_params_get_metric_type(vamana_params) ==
|
||||
ZVEC_METRIC_TYPE_L2);
|
||||
|
||||
int max_degree, search_list_size;
|
||||
float alpha;
|
||||
bool saturate_graph, use_contiguous_memory;
|
||||
zvec_error_code_t verr;
|
||||
|
||||
// Set and get Vamana params
|
||||
verr = zvec_index_params_set_vamana_params(vamana_params, 128, 200, 1.5f,
|
||||
true, true);
|
||||
TEST_ASSERT(verr == ZVEC_OK);
|
||||
verr = zvec_index_params_get_vamana_params(
|
||||
vamana_params, &max_degree, &search_list_size, &alpha, &saturate_graph,
|
||||
&use_contiguous_memory);
|
||||
TEST_ASSERT(verr == ZVEC_OK);
|
||||
TEST_ASSERT(max_degree == 128);
|
||||
TEST_ASSERT(search_list_size == 200);
|
||||
TEST_ASSERT(alpha == 1.5f);
|
||||
TEST_ASSERT(saturate_graph == true);
|
||||
TEST_ASSERT(use_contiguous_memory == true);
|
||||
|
||||
// Set metric and quantize type
|
||||
zvec_index_params_set_metric_type(vamana_params, ZVEC_METRIC_TYPE_COSINE);
|
||||
TEST_ASSERT(zvec_index_params_get_metric_type(vamana_params) ==
|
||||
ZVEC_METRIC_TYPE_COSINE);
|
||||
|
||||
// Test type mismatch: set_vamana_params on non-Vamana params should fail
|
||||
verr = zvec_index_params_set_vamana_params(hnsw_params, 64, 100, 1.2f, false,
|
||||
false);
|
||||
TEST_ASSERT(verr == ZVEC_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
// Cleanup
|
||||
zvec_index_params_destroy(hnsw_params);
|
||||
zvec_index_params_destroy(ivf_params);
|
||||
zvec_index_params_destroy(flat_params);
|
||||
zvec_index_params_destroy(invert_params);
|
||||
zvec_index_params_destroy(vamana_params);
|
||||
|
||||
TEST_END();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue