diff --git a/c/lib.cpp b/c/lib.cpp index a35acaa6..931cb62d 100644 --- a/c/lib.cpp +++ b/c/lib.cpp @@ -6,6 +6,13 @@ extern "C" { #include "usearch.h" } +// Check if NDEBUG is defined to determine if it's a release build +#ifdef NDEBUG +#define USEARCH_ASSERT(expression) (void)(expression) +#else +#define USEARCH_ASSERT(expression) assert(expression) +#endif + using namespace unum::usearch; using namespace unum; @@ -106,7 +113,7 @@ extern "C" { USEARCH_EXPORT usearch_index_t usearch_init(usearch_init_options_t* options, usearch_error_t* error) { - assert(options && error && "Missing arguments"); + USEARCH_ASSERT(options && error && "Missing arguments"); index_dense_config_t config(options->connectivity, options->expansion_add, options->expansion_search); config.multi = options->multi; @@ -136,13 +143,13 @@ USEARCH_EXPORT void usearch_free(usearch_index_t index, usearch_error_t*) { } USEARCH_EXPORT size_t usearch_serialized_length(usearch_index_t index, usearch_error_t*) { - assert(index && "Missing arguments"); + USEARCH_ASSERT(index && "Missing arguments"); return reinterpret_cast(index)->serialized_length(); } USEARCH_EXPORT void usearch_save(usearch_index_t index, char const* path, usearch_error_t* error) { - assert(index && path && error && "Missing arguments"); + USEARCH_ASSERT(index && path && error && "Missing arguments"); serialization_result_t result = reinterpret_cast(index)->save(path); if (!result) *error = result.error.release(); @@ -150,7 +157,7 @@ USEARCH_EXPORT void usearch_save(usearch_index_t index, char const* path, usearc USEARCH_EXPORT void usearch_load(usearch_index_t index, char const* path, usearch_error_t* error) { - assert(index && path && error && "Missing arguments"); + USEARCH_ASSERT(index && path && error && "Missing arguments"); serialization_result_t result = reinterpret_cast(index)->load(path); if (!result) *error = result.error.release(); @@ -158,7 +165,7 @@ USEARCH_EXPORT void usearch_load(usearch_index_t index, char const* path, usearc USEARCH_EXPORT void usearch_view(usearch_index_t index, char const* path, usearch_error_t* error) { - assert(index && path && error && "Missing arguments"); + USEARCH_ASSERT(index && path && error && "Missing arguments"); serialization_result_t result = reinterpret_cast(index)->view(path); if (!result) *error = result.error.release(); @@ -166,7 +173,7 @@ USEARCH_EXPORT void usearch_view(usearch_index_t index, char const* path, usearc USEARCH_EXPORT void usearch_metadata(char const* path, usearch_init_options_t* options, usearch_error_t* error) { - assert(path && options && error && "Missing arguments"); + USEARCH_ASSERT(path && options && error && "Missing arguments"); index_dense_metadata_result_t result = index_dense_metadata_from_path(path); if (!result) *error = result.error.release(); @@ -184,7 +191,7 @@ USEARCH_EXPORT void usearch_metadata(char const* path, usearch_init_options_t* o USEARCH_EXPORT void usearch_save_buffer(usearch_index_t index, void* buffer, size_t length, usearch_error_t* error) { - assert(index && buffer && length && error && "Missing arguments"); + USEARCH_ASSERT(index && buffer && length && error && "Missing arguments"); memory_mapped_file_t memory_map((byte_t*)buffer, length); serialization_result_t result = reinterpret_cast(index)->save(std::move(memory_map)); if (!result) @@ -194,7 +201,7 @@ USEARCH_EXPORT void usearch_save_buffer(usearch_index_t index, void* buffer, siz USEARCH_EXPORT void usearch_load_buffer(usearch_index_t index, void const* buffer, size_t length, usearch_error_t* error) { - assert(index && buffer && length && error && "Missing arguments"); + USEARCH_ASSERT(index && buffer && length && error && "Missing arguments"); memory_mapped_file_t memory_map((byte_t*)buffer, length); serialization_result_t result = reinterpret_cast(index)->load(std::move(memory_map)); if (!result) @@ -204,7 +211,7 @@ USEARCH_EXPORT void usearch_load_buffer(usearch_index_t index, void const* buffe USEARCH_EXPORT void usearch_view_buffer(usearch_index_t index, void const* buffer, size_t length, usearch_error_t* error) { - assert(index && buffer && length && error && "Missing arguments"); + USEARCH_ASSERT(index && buffer && length && error && "Missing arguments"); memory_mapped_file_t memory_map((byte_t*)buffer, length); serialization_result_t result = reinterpret_cast(index)->view(std::move(memory_map)); if (!result) @@ -214,7 +221,7 @@ USEARCH_EXPORT void usearch_view_buffer(usearch_index_t index, void const* buffe USEARCH_EXPORT void usearch_metadata_buffer(void const* buffer, size_t length, usearch_init_options_t* options, usearch_error_t* error) { - assert(buffer && length && options && error && "Missing arguments"); + USEARCH_ASSERT(buffer && length && options && error && "Missing arguments"); index_dense_metadata_result_t result = index_dense_metadata_from_buffer(memory_mapped_file_t((byte_t*)(buffer), length)); if (!result) @@ -231,24 +238,65 @@ USEARCH_EXPORT void usearch_metadata_buffer(void const* buffer, size_t length, u options->metric = NULL; } -USEARCH_EXPORT size_t usearch_size(usearch_index_t index, usearch_error_t*) { // +USEARCH_EXPORT size_t usearch_size(usearch_index_t index, usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); return reinterpret_cast(index)->size(); } -USEARCH_EXPORT size_t usearch_capacity(usearch_index_t index, usearch_error_t*) { +USEARCH_EXPORT size_t usearch_capacity(usearch_index_t index, usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); return reinterpret_cast(index)->capacity(); } -USEARCH_EXPORT size_t usearch_dimensions(usearch_index_t index, usearch_error_t*) { +USEARCH_EXPORT size_t usearch_dimensions(usearch_index_t index, usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); return reinterpret_cast(index)->dimensions(); } -USEARCH_EXPORT size_t usearch_connectivity(usearch_index_t index, usearch_error_t*) { +USEARCH_EXPORT size_t usearch_connectivity(usearch_index_t index, usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); return reinterpret_cast(index)->connectivity(); } +USEARCH_EXPORT size_t usearch_expansion_add(usearch_index_t index, usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); + return reinterpret_cast(index)->expansion_add(); +} + +USEARCH_EXPORT size_t usearch_expansion_search(usearch_index_t index, usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); + return reinterpret_cast(index)->expansion_search(); +} + +USEARCH_EXPORT void usearch_change_expansion_add(usearch_index_t index, size_t expansion, usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); + reinterpret_cast(index)->change_expansion_add(expansion); +} + +USEARCH_EXPORT void usearch_change_expansion_search(usearch_index_t index, size_t expansion, usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); + reinterpret_cast(index)->change_expansion_search(expansion); +} + +USEARCH_EXPORT void usearch_change_metric_kind(usearch_index_t index, usearch_metric_kind_t kind, + usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); + auto& index_dense = *reinterpret_cast(index); + index_dense.change_metric( + metric_punned_t(index_dense.dimensions(), metric_kind_to_cpp(kind), index_dense.scalar_kind())); +} + +USEARCH_EXPORT void usearch_change_metric(usearch_index_t index, usearch_metric_t metric, usearch_metric_kind_t kind, + usearch_error_t* error) { + USEARCH_ASSERT(index && error && "Missing arguments"); + auto& index_dense = *reinterpret_cast(index); + index_dense.change_metric(metric_punned_t(index_dense.dimensions(), reinterpret_cast(metric), + metric_punned_signature_t::array_array_k, metric_kind_to_cpp(kind), + index_dense.scalar_kind())); +} + USEARCH_EXPORT void usearch_reserve(usearch_index_t index, size_t capacity, usearch_error_t* error) { - assert(index && error && "Missing arguments"); + USEARCH_ASSERT(index && error && "Missing arguments"); if (!reinterpret_cast(index)->reserve(capacity)) *error = "Out of memory!"; } @@ -257,19 +305,19 @@ USEARCH_EXPORT void usearch_add( usearch_index_t index, usearch_key_t key, void const* vector, usearch_scalar_kind_t kind, // usearch_error_t* error) { - assert(index && vector && error && "Missing arguments"); + USEARCH_ASSERT(index && vector && error && "Missing arguments"); add_result_t result = add_(reinterpret_cast(index), key, vector, scalar_kind_to_cpp(kind)); if (!result) *error = result.error.release(); } USEARCH_EXPORT bool usearch_contains(usearch_index_t index, usearch_key_t key, usearch_error_t*) { - assert(index && "Missing arguments"); + USEARCH_ASSERT(index && "Missing arguments"); return reinterpret_cast(index)->contains(key); } USEARCH_EXPORT size_t usearch_count(usearch_index_t index, usearch_key_t key, usearch_error_t*) { - assert(index && "Missing arguments"); + USEARCH_ASSERT(index && "Missing arguments"); return reinterpret_cast(index)->count(key); } @@ -277,7 +325,7 @@ USEARCH_EXPORT size_t usearch_search( usearch_index_t index, void const* vector, usearch_scalar_kind_t kind, size_t results_limit, // usearch_key_t* found_keys, usearch_distance_t* found_distances, usearch_error_t* error) { - assert(index && vector && error && "Missing arguments"); + USEARCH_ASSERT(index && vector && error && "Missing arguments"); search_result_t result = search_(reinterpret_cast(index), vector, scalar_kind_to_cpp(kind), results_limit); if (!result) { @@ -292,13 +340,13 @@ USEARCH_EXPORT size_t usearch_get( // usearch_index_t index, usearch_key_t key, size_t count, // void* vectors, usearch_scalar_kind_t kind, usearch_error_t*) { - assert(index && vectors); + USEARCH_ASSERT(index && vectors); return get_(reinterpret_cast(index), key, count, vectors, scalar_kind_to_cpp(kind)); } USEARCH_EXPORT size_t usearch_remove(usearch_index_t index, usearch_key_t key, usearch_error_t* error) { - assert(index && error && "Missing arguments"); + USEARCH_ASSERT(index && error && "Missing arguments"); labeling_result_t result = reinterpret_cast(index)->remove(key); if (!result) *error = result.error.release(); @@ -308,7 +356,7 @@ USEARCH_EXPORT size_t usearch_remove(usearch_index_t index, usearch_key_t key, u USEARCH_EXPORT size_t usearch_rename( // usearch_index_t index, usearch_key_t from, usearch_key_t to, usearch_error_t* error) { - assert(index && error && "Missing arguments"); + USEARCH_ASSERT(index && error && "Missing arguments"); labeling_result_t result = reinterpret_cast(index)->rename(from, to); if (!result) *error = result.error.release(); @@ -334,7 +382,7 @@ USEARCH_EXPORT void usearch_exact_search( // usearch_distance_t* distances, size_t distances_stride, // usearch_error_t* error) { - assert(dataset && queries && keys && distances && error && "Missing arguments"); + USEARCH_ASSERT(dataset && queries && keys && distances && error && "Missing arguments"); metric_punned_t metric(dimensions, metric_kind_to_cpp(metric_kind), scalar_kind_to_cpp(scalar_kind)); executor_default_t executor(threads); diff --git a/c/usearch.h b/c/usearch.h index e5f9f2ba..f67e7936 100644 --- a/c/usearch.h +++ b/c/usearch.h @@ -227,6 +227,57 @@ USEARCH_EXPORT size_t usearch_connectivity(usearch_index_t index, usearch_error_ */ USEARCH_EXPORT void usearch_reserve(usearch_index_t index, size_t capacity, usearch_error_t* error); +/** + * @brief Retrieves the expansion value used during index creation. + * @param[in] index The handle to the USearch index to be queried. + * @param[out] error Pointer to a string where the error message will be stored, if an error occurs. + * @return The expansion value used during index creation. + */ +USEARCH_EXPORT size_t usearch_expansion_add(usearch_index_t index, usearch_error_t* error); + +/** + * @brief Retrieves the expansion value used during search. + * @param[in] index The handle to the USearch index to be queried. + * @param[out] error Pointer to a string where the error message will be stored, if an error occurs. + * @return The expansion value used during search. + */ +USEARCH_EXPORT size_t usearch_expansion_search(usearch_index_t index, usearch_error_t* error); + +/** + * @brief Updates the expansion value used during index creation. Rarely used. + * @param[in] index The handle to the USearch index to be queried. + * @param[in] expansion The new expansion value. + * @param[out] error Pointer to a string where the error message will be stored, if an error occurs. + */ +USEARCH_EXPORT void usearch_change_expansion_add(usearch_index_t index, size_t expansion, usearch_error_t* error); + +/** + * @brief Updates the expansion value used during search. Rarely used. + * @param[in] index The handle to the USearch index to be queried. + * @param[in] expansion The new expansion value. + * @param[out] error Pointer to a string where the error message will be stored, if an error occurs. + */ +USEARCH_EXPORT void usearch_change_expansion_search(usearch_index_t index, size_t expansion, usearch_error_t* error); + +/** + * @brief Updates the metric kind used for distance calculation between vectors. + * @param[in] index The handle to the USearch index to be queried. + * @param[in] kind The metric kind used for distance calculation between vectors. + * @param[out] error Pointer to a string where the error message will be stored, if an error occurs. + */ +USEARCH_EXPORT void usearch_change_metric_kind(usearch_index_t index, usearch_metric_kind_t kind, + usearch_error_t* error); + +/** + * @brief Updates the custom metric function used for distance calculation between vectors. + * @param[in] index The handle to the USearch index to be queried. + * @param[in] metric The custom metric function used for distance calculation between vectors. + * @param[in] kind The metric kind used for distance calculation between vectors. Needed for serialization. + * @param[out] error Pointer to a string where the error message will be stored, if an error occurs. + */ +USEARCH_EXPORT void usearch_change_metric(usearch_index_t index, usearch_metric_t metric, usearch_metric_kind_t kind, + usearch_error_t* error); + /** * @brief Adds a vector with a key to the index. * @param[inout] index The handle to the USearch index to be populated.