deglib 0.2.5__tar.gz → 0.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deglib-0.2.5 → deglib-0.2.6}/CMakeLists.txt +11 -2
- {deglib-0.2.5/src/deglib.egg-info → deglib-0.2.6}/PKG-INFO +16 -5
- {deglib-0.2.5 → deglib-0.2.6}/README.md +15 -4
- {deglib-0.2.5 → deglib-0.2.6}/lib/CMakeLists.txt +4 -5
- {deglib-0.2.5 → deglib-0.2.6}/lib/bench/include/build.h +3 -1
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/builder.h +101 -2
- deglib-0.2.6/lib/deglib/include/deglib/deglib.h +118 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/fp16_ip.h +30 -2
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/fp16_l2.h +32 -1
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/fp32_ip.h +33 -2
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/fp32_l2.h +32 -2
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/int8_ip.h +76 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/uint8_ip.h +84 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/graph/dynamic_graph.h +12 -96
- deglib-0.2.6/lib/deglib/include/deglib/graph/internal_graph.h +912 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/graph/readonly_graph.h +69 -111
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/graph/sizebounded_graph.h +44 -176
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/graph.h +12 -46
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/optimization/flas/junker_volgenant_solver.h +4 -2
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/optimization/quantization/evp_quantize.h +96 -0
- deglib-0.2.6/lib/deglib/include/deglib/optimization/quantization/quantizer_concept.h +41 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/optimization/quantization/scalar_quantize.h +137 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/optimization.h +6 -23
- deglib-0.2.6/lib/deglib/include/deglib/search/kmeans.h +428 -0
- deglib-0.2.6/lib/deglib/include/deglib/search/result_list.h +240 -0
- deglib-0.2.6/lib/deglib/include/deglib/search/searcher.h +902 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/search.h +13 -24
- {deglib-0.2.5 → deglib-0.2.6}/setup.py +28 -24
- {deglib-0.2.5 → deglib-0.2.6}/src/deg_cpp/deglib_cpp.cpp +120 -80
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib/__init__.py +1 -3
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib/graph.py +8 -13
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib/optimization.py +5 -17
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib/search.py +30 -5
- {deglib-0.2.5 → deglib-0.2.6/src/deglib.egg-info}/PKG-INFO +16 -5
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib.egg-info/SOURCES.txt +3 -0
- {deglib-0.2.5 → deglib-0.2.6}/tests/test_builder.py +47 -0
- {deglib-0.2.5 → deglib-0.2.6}/tests/test_graph.py +3 -7
- {deglib-0.2.5 → deglib-0.2.6}/tests/test_searcher.py +65 -18
- deglib-0.2.5/lib/deglib/include/deglib/deglib.h +0 -17
- deglib-0.2.5/lib/deglib/include/deglib/graph/internal_graph.h +0 -528
- deglib-0.2.5/lib/deglib/include/deglib/search/searcher.h +0 -493
- {deglib-0.2.5 → deglib-0.2.6}/LICENSE +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/MANIFEST.in +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/bench/CMakeLists.txt +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/bench/include/benchmark.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/bench/include/dataset.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/bench/include/file_io.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/bench/include/logging.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/bench/include/repository.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/bench/include/stats.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/bench/include/stopwatch.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/cmake_modules/DetectCPUFeatures.cmake +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/CMakeLists.txt +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/analysis.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/concurrent.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/config.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/evp_ip.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/fp16.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/fp32.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/int8.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/int8_l2.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/residual_mode.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/uint8.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distance/uint8_l2.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/distances.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/filter.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/graph/intrusive_list.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/graph/mutable_graph.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/graph/visited_list_pool.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/optimization/flas/fast_linear_assignment_sorter.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/optimization/flas/fast_linear_assignment_sorter_mt.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/optimization/pruning.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/optimization/transform.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/utils/cpu.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/utils/memory.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/deglib/include/deglib/utils/random.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/test/CMakeLists.txt +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/lib/test/src/common/test_helpers.h +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/pyproject.toml +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/setup.cfg +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib/analysis.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib/builder.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib/cpu.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib/distances.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib/utils.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib.egg-info/dependency_links.txt +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib.egg-info/requires.txt +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/src/deglib.egg-info/top_level.txt +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/tests/test_analysis.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/tests/test_filter.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/tests/test_presort.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/tests/test_rerank.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/tests/test_scalar_quantizer.py +0 -0
- {deglib-0.2.5 → deglib-0.2.6}/tests/test_transform.py +0 -0
|
@@ -15,11 +15,20 @@ find_package(pybind11 CONFIG REQUIRED)
|
|
|
15
15
|
set(ENABLE_BENCHMARKS OFF CACHE BOOL "Disable benchmarks" FORCE)
|
|
16
16
|
set(ENABLE_TESTING OFF CACHE BOOL "Disable tests" FORCE)
|
|
17
17
|
|
|
18
|
+
# Find deglib cpp source: prefer ../cpp in repository checkouts, fallback to local lib/ (PyPI sdist)
|
|
19
|
+
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../cpp/CMakeLists.txt")
|
|
20
|
+
set(DEGLIB_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../cpp")
|
|
21
|
+
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/CMakeLists.txt")
|
|
22
|
+
set(DEGLIB_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lib")
|
|
23
|
+
else()
|
|
24
|
+
message(FATAL_ERROR "Could not find deglib cpp source directory (checked ../cpp/ and lib/)")
|
|
25
|
+
endif()
|
|
26
|
+
|
|
18
27
|
# Add cpp-deglib
|
|
19
|
-
add_subdirectory(${
|
|
28
|
+
add_subdirectory(${DEGLIB_CPP_DIR} deglib_cpp_build)
|
|
20
29
|
pybind11_add_module(deglib_cpp src/deg_cpp/deglib_cpp.cpp)
|
|
21
30
|
|
|
22
31
|
# Specify the include directories
|
|
23
|
-
include_directories(${
|
|
32
|
+
include_directories(${DEGLIB_CPP_DIR}/deglib/include)
|
|
24
33
|
|
|
25
34
|
target_link_libraries(deglib_cpp PRIVATE compile-options)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deglib
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Python bindings for the Dynamic Exploration Graph library by Nico Hezel
|
|
5
5
|
Author: Nico Hezel
|
|
6
6
|
Author-email: Bruno Schilling <bruno.schilling@htw-berlin.de>
|
|
@@ -278,10 +278,21 @@ Controls the topology optimization strategy:
|
|
|
278
278
|
- `OptimizationTarget.HighLID`: Optimized for datasets with high local intrinsic dimensionality (supports multithreaded building).
|
|
279
279
|
- `OptimizationTarget.StreamingData`: Optimized for continuous dynamic additions and deletions.
|
|
280
280
|
|
|
281
|
-
### Search Parameter `
|
|
282
|
-
|
|
283
|
-
-
|
|
284
|
-
-
|
|
281
|
+
### Unified Search Parameter: `eps_or_ef`
|
|
282
|
+
`Searcher` provides a unified exploration parameter `eps_or_ef`:
|
|
283
|
+
- **Relative Distance Margin (`eps_or_ef < 1.0`)**: Standard DEG dynamic distance exploration bound $(1 + \epsilon) \cdot d_{\text{cur}}$.
|
|
284
|
+
- Small values (e.g. `eps_or_ef=0.001` or `0.01`): Faster query execution.
|
|
285
|
+
- Higher values (e.g. `eps_or_ef=0.1` to `0.3`): Higher recall rate.
|
|
286
|
+
- **Fixed Candidate Pool (`eps_or_ef >= 1.0`)**: HNSW-style search with a fixed candidate pool size of `round(eps_or_ef)` during exploration.
|
|
287
|
+
- Typical values: `64`, `96`, `128`, `200`, `400`.
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
# Mode 1: Relative distance margin (default: eps_or_ef=0.1)
|
|
291
|
+
indices, distances = searcher.search(query, k=10, eps_or_ef=0.1)
|
|
292
|
+
|
|
293
|
+
# Mode 2: Fixed candidate pool (HNSW-style)
|
|
294
|
+
indices, distances = searcher.search(query, k=10, eps_or_ef=128)
|
|
295
|
+
```
|
|
285
296
|
|
|
286
297
|
### Supported Metrics & Data Types
|
|
287
298
|
- `Metric.FP32_L2`: Euclidean distance (`np.float32`)
|
|
@@ -250,10 +250,21 @@ Controls the topology optimization strategy:
|
|
|
250
250
|
- `OptimizationTarget.HighLID`: Optimized for datasets with high local intrinsic dimensionality (supports multithreaded building).
|
|
251
251
|
- `OptimizationTarget.StreamingData`: Optimized for continuous dynamic additions and deletions.
|
|
252
252
|
|
|
253
|
-
### Search Parameter `
|
|
254
|
-
|
|
255
|
-
-
|
|
256
|
-
-
|
|
253
|
+
### Unified Search Parameter: `eps_or_ef`
|
|
254
|
+
`Searcher` provides a unified exploration parameter `eps_or_ef`:
|
|
255
|
+
- **Relative Distance Margin (`eps_or_ef < 1.0`)**: Standard DEG dynamic distance exploration bound $(1 + \epsilon) \cdot d_{\text{cur}}$.
|
|
256
|
+
- Small values (e.g. `eps_or_ef=0.001` or `0.01`): Faster query execution.
|
|
257
|
+
- Higher values (e.g. `eps_or_ef=0.1` to `0.3`): Higher recall rate.
|
|
258
|
+
- **Fixed Candidate Pool (`eps_or_ef >= 1.0`)**: HNSW-style search with a fixed candidate pool size of `round(eps_or_ef)` during exploration.
|
|
259
|
+
- Typical values: `64`, `96`, `128`, `200`, `400`.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
# Mode 1: Relative distance margin (default: eps_or_ef=0.1)
|
|
263
|
+
indices, distances = searcher.search(query, k=10, eps_or_ef=0.1)
|
|
264
|
+
|
|
265
|
+
# Mode 2: Fixed candidate pool (HNSW-style)
|
|
266
|
+
indices, distances = searcher.search(query, k=10, eps_or_ef=128)
|
|
267
|
+
```
|
|
257
268
|
|
|
258
269
|
### Supported Metrics & Data Types
|
|
259
270
|
- `Metric.FP32_L2`: Euclidean distance (`np.float32`)
|
|
@@ -70,12 +70,11 @@ else() # Native build
|
|
|
70
70
|
|
|
71
71
|
# setup compiler flags
|
|
72
72
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
73
|
+
# Portable native build: generic code is compiled for the build machine's
|
|
74
|
+
# baseline ISA only (no explicit AVX-512), so the module imports cleanly on
|
|
75
|
+
# CPUs without AVX-512. AVX-512 kernels are per-function target attributes
|
|
76
|
+
# (DEGLIB_TARGET_AVX512*), gated at runtime by deglib::cpu::select_dist().
|
|
73
77
|
target_compile_options(compile-options INTERFACE -O3 -march=native -fpic -w -pthread -ftree-vectorize)
|
|
74
|
-
if(SUPPORT_AVX512F)
|
|
75
|
-
target_compile_options(compile-options INTERFACE -mavx512f -mfma)
|
|
76
|
-
elseif(SUPPORT_AVX2)
|
|
77
|
-
target_compile_options(compile-options INTERFACE -mavx2 -mfma)
|
|
78
|
-
endif()
|
|
79
78
|
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
|
80
79
|
target_compile_options(compile-options INTERFACE -O2 -DNDEBUG -march=native -fpic)
|
|
81
80
|
elseif(MSVC)
|
|
@@ -41,7 +41,9 @@ inline deglib::graph::SizeBoundedGraph create_random_graph(
|
|
|
41
41
|
const uint32_t vertex_count = (max_size > 0 && max_size < repository.size()) ? max_size : uint32_t(repository.size());
|
|
42
42
|
const auto feature_data = repository.getFeature(0);
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
auto graph = deglib::graph::SizeBoundedGraph(vertex_count, k, feature_space);
|
|
45
|
+
deglib::builder::populate_random_graph(graph, feature_data, vertex_count);
|
|
46
|
+
return graph;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
inline void create_graph(
|
|
@@ -649,7 +649,7 @@ class EvenRegularGraphBuilder {
|
|
|
649
649
|
const auto dist_func_param = graph.getFeatureSpace().get_dist_func_param();
|
|
650
650
|
|
|
651
651
|
// find good neighbors for the new vertex
|
|
652
|
-
const
|
|
652
|
+
const auto entry_vertex_indices = graph.getEntryVertexIndices();
|
|
653
653
|
auto top_list =
|
|
654
654
|
graph.search_intern(entry_vertex_indices, new_vertex_feature, std::max(uint32_t(this->extend_k_), edges_per_vertex), this->extend_eps_);
|
|
655
655
|
const auto results = topListAscending(top_list);
|
|
@@ -1535,7 +1535,7 @@ DynamicExplorationGraph build_from_data(
|
|
|
1535
1535
|
}
|
|
1536
1536
|
|
|
1537
1537
|
auto feature_space = deglib::distances::FloatSpace(dims, metric);
|
|
1538
|
-
auto graph = deglib::
|
|
1538
|
+
auto graph = DynamicExplorationGraph(std::make_unique<deglib::graph::SizeBoundedGraph>(vertex_count, edges_per_vertex, feature_space));
|
|
1539
1539
|
|
|
1540
1540
|
std::mt19937 rng(seed);
|
|
1541
1541
|
auto builder = EvenRegularGraphBuilder(
|
|
@@ -1555,4 +1555,103 @@ DynamicExplorationGraph build_from_data(
|
|
|
1555
1555
|
return graph;
|
|
1556
1556
|
}
|
|
1557
1557
|
|
|
1558
|
+
/**
|
|
1559
|
+
* Populate a MutableGraph with a random k-regular graph.
|
|
1560
|
+
*
|
|
1561
|
+
* @param graph The mutable graph instance to populate.
|
|
1562
|
+
* @param feature_data Pointer to contiguous feature data bytes.
|
|
1563
|
+
* @param vertex_count Number of vertices to insert.
|
|
1564
|
+
* @param seed Random seed for deterministic graph construction (default: 7).
|
|
1565
|
+
*/
|
|
1566
|
+
inline void populate_random_graph(
|
|
1567
|
+
deglib::graph::MutableGraph& graph,
|
|
1568
|
+
const std::byte* feature_data,
|
|
1569
|
+
const uint32_t vertex_count,
|
|
1570
|
+
const uint32_t seed = 7
|
|
1571
|
+
) {
|
|
1572
|
+
const auto& feature_space = graph.getFeatureSpace();
|
|
1573
|
+
const auto edges_per_vertex = graph.getEdgesPerVertex();
|
|
1574
|
+
const auto dist_func = feature_space.get_dist_func();
|
|
1575
|
+
const auto dist_func_param = feature_space.get_dist_func_param();
|
|
1576
|
+
|
|
1577
|
+
// add the initial vertices (edges_per_vertex + 1)
|
|
1578
|
+
{
|
|
1579
|
+
const auto size = static_cast<uint32_t>(edges_per_vertex + 1);
|
|
1580
|
+
for (uint32_t y = 0; y < size; y++) {
|
|
1581
|
+
const auto query = feature_data + size_t(y) * feature_space.get_data_size();
|
|
1582
|
+
const auto internal_index = graph.addVertex(y, query);
|
|
1583
|
+
|
|
1584
|
+
auto neighbor_indices = std::vector<uint32_t>();
|
|
1585
|
+
auto neighbor_weights = std::vector<float>();
|
|
1586
|
+
for (uint32_t x = 0; x < size; x++) {
|
|
1587
|
+
if (x == internal_index) continue;
|
|
1588
|
+
neighbor_indices.emplace_back(x);
|
|
1589
|
+
neighbor_weights.emplace_back(dist_func(query, feature_data + size_t(x) * feature_space.get_data_size(), dist_func_param));
|
|
1590
|
+
}
|
|
1591
|
+
graph.changeEdges(internal_index, neighbor_indices.data(), neighbor_weights.data());
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// random order of vertices
|
|
1596
|
+
auto rnd = std::mt19937(seed);
|
|
1597
|
+
auto rnd_neighbor = deglib::random::DeterministicUniformIntDistribution<uint32_t>(0, edges_per_vertex - 1);
|
|
1598
|
+
|
|
1599
|
+
// add the remaining vertices
|
|
1600
|
+
for (uint32_t label = edges_per_vertex + 1; label < vertex_count; label++) {
|
|
1601
|
+
const auto new_vertex_feature = feature_data + size_t(label) * feature_space.get_data_size();
|
|
1602
|
+
const auto internal_index = graph.addVertex(label, new_vertex_feature);
|
|
1603
|
+
auto top_list = deglib::random::DeterministicUniformIntDistribution<uint32_t>(0, label - 1);
|
|
1604
|
+
|
|
1605
|
+
// remove the worst edge of the good neighbors and connect them with this new vertex
|
|
1606
|
+
auto new_neighbors = std::vector<std::pair<uint32_t, float>>();
|
|
1607
|
+
while (new_neighbors.size() < edges_per_vertex) {
|
|
1608
|
+
const auto candidate_index = static_cast<uint32_t>(top_list(rnd));
|
|
1609
|
+
|
|
1610
|
+
// check if the vertex is already in the edge list of the new vertex (added during a previous loop-run)
|
|
1611
|
+
// since all edges are undirected and the edge information of the new vertex does not yet exist, we search the other way around.
|
|
1612
|
+
if (graph.hasEdge(candidate_index, internal_index)) continue;
|
|
1613
|
+
|
|
1614
|
+
// find a new random neighbor
|
|
1615
|
+
uint32_t new_neighbor_index = 0;
|
|
1616
|
+
float new_neighbor_weight = 0;
|
|
1617
|
+
bool found = false;
|
|
1618
|
+
const auto neighbor_weights = graph.getNeighborWeights(candidate_index);
|
|
1619
|
+
const auto neighbor_indices = graph.getNeighborIndices(candidate_index);
|
|
1620
|
+
while (!found) {
|
|
1621
|
+
const auto edge_idx = static_cast<uint32_t>(rnd_neighbor(rnd));
|
|
1622
|
+
const auto neighbor_index = neighbor_indices[edge_idx];
|
|
1623
|
+
const auto neighbor_weight = neighbor_weights[edge_idx];
|
|
1624
|
+
|
|
1625
|
+
// the suggested neighbors might already be in the edge list of the new vertex
|
|
1626
|
+
if (!graph.hasEdge(neighbor_index, internal_index)) {
|
|
1627
|
+
new_neighbor_index = neighbor_index;
|
|
1628
|
+
new_neighbor_weight = neighbor_weight;
|
|
1629
|
+
found = true;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
// place the new vertex in the edge list of the result-vertex
|
|
1634
|
+
const auto candidate_dist = dist_func(new_vertex_feature, graph.getFeatureVector(candidate_index), dist_func_param);
|
|
1635
|
+
graph.changeEdge(candidate_index, new_neighbor_index, internal_index, candidate_dist);
|
|
1636
|
+
new_neighbors.emplace_back(candidate_index, candidate_dist);
|
|
1637
|
+
|
|
1638
|
+
// place the new vertex in the edge list of the worst edge neighbor
|
|
1639
|
+
const auto new_neighbor_dist = dist_func(new_vertex_feature, graph.getFeatureVector(new_neighbor_index), dist_func_param);
|
|
1640
|
+
graph.changeEdge(new_neighbor_index, candidate_index, internal_index, new_neighbor_dist);
|
|
1641
|
+
new_neighbors.emplace_back(new_neighbor_index, new_neighbor_dist);
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
// sort the neighbors by their neighbor indices and store them in the new vertex
|
|
1645
|
+
std::sort(new_neighbors.begin(), new_neighbors.end(), [](const auto& x, const auto& y) { return x.first < y.first; });
|
|
1646
|
+
auto neighbor_indices = std::vector<uint32_t>();
|
|
1647
|
+
auto neighbor_weights = std::vector<float>();
|
|
1648
|
+
for (auto&& neighbor : new_neighbors) {
|
|
1649
|
+
neighbor_indices.emplace_back(neighbor.first);
|
|
1650
|
+
neighbor_weights.emplace_back(neighbor.second);
|
|
1651
|
+
}
|
|
1652
|
+
graph.changeEdges(internal_index, neighbor_indices.data(), neighbor_weights.data());
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1558
1656
|
} // end namespace deglib::builder
|
|
1657
|
+
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "deglib/analysis.h"
|
|
4
|
+
#include "deglib/builder.h"
|
|
5
|
+
#include "deglib/config.h"
|
|
6
|
+
#include "deglib/distances.h"
|
|
7
|
+
#include "deglib/filter.h"
|
|
8
|
+
#include "deglib/graph.h"
|
|
9
|
+
#include "deglib/optimization.h"
|
|
10
|
+
#include "deglib/search.h"
|
|
11
|
+
#include "deglib/search/searcher.h"
|
|
12
|
+
#include "deglib/utils/memory.h"
|
|
13
|
+
#include "deglib/utils/random.h"
|
|
14
|
+
|
|
15
|
+
namespace deglib {
|
|
16
|
+
using builder::build_from_data;
|
|
17
|
+
using builder::EvenRegularGraphBuilder;
|
|
18
|
+
using builder::OptimizationTarget;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create an EvenRegularGraphBuilder for building and incrementally modifying a DynamicExplorationGraph.
|
|
22
|
+
*/
|
|
23
|
+
inline builder::EvenRegularGraphBuilder create_builder(
|
|
24
|
+
DynamicExplorationGraph& graph,
|
|
25
|
+
std::mt19937& rnd,
|
|
26
|
+
const builder::OptimizationTarget optimization_target = builder::OptimizationTarget::LowLID,
|
|
27
|
+
const uint8_t extend_k = 0,
|
|
28
|
+
const float extend_eps = 0.1f,
|
|
29
|
+
const uint8_t improve_k = 0,
|
|
30
|
+
const float improve_eps = 0.001f,
|
|
31
|
+
const uint8_t max_path_length = 5,
|
|
32
|
+
const uint32_t improve_tries = 0
|
|
33
|
+
) {
|
|
34
|
+
return builder::EvenRegularGraphBuilder(
|
|
35
|
+
graph, rnd, optimization_target, extend_k, extend_eps, improve_k, improve_eps, max_path_length, improve_tries
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create an empty mutable DynamicExplorationGraph with the given capacity,
|
|
41
|
+
* edges per vertex, and feature space (backed by SizeBoundedGraph).
|
|
42
|
+
*/
|
|
43
|
+
inline DynamicExplorationGraph create_empty(
|
|
44
|
+
const uint32_t max_vertex_count,
|
|
45
|
+
const uint8_t edges_per_vertex,
|
|
46
|
+
const deglib::distances::FloatSpace& feature_space
|
|
47
|
+
) {
|
|
48
|
+
auto graph = std::make_unique<deglib::graph::SizeBoundedGraph>(max_vertex_count, edges_per_vertex, feature_space);
|
|
49
|
+
return DynamicExplorationGraph(std::move(graph));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Create an empty mutable DynamicExplorationGraph with chunk-based dynamic memory allocation (backed by DynamicGraph).
|
|
54
|
+
*
|
|
55
|
+
* @param edges_per_vertex Number of edges per vertex (must be even).
|
|
56
|
+
* @param feature_space The feature space defining dimensionality and metric.
|
|
57
|
+
* @param chunk_size Target number of vertices per memory chunk (default = 1024).
|
|
58
|
+
* Will be automatically rounded up to the nearest power of 2 (e.g. 600 -> 1024).
|
|
59
|
+
*/
|
|
60
|
+
inline DynamicExplorationGraph create_dynamic_empty(
|
|
61
|
+
const uint8_t edges_per_vertex,
|
|
62
|
+
const deglib::distances::FloatSpace& feature_space,
|
|
63
|
+
const uint32_t chunk_size = 1024
|
|
64
|
+
) {
|
|
65
|
+
auto graph = std::make_unique<deglib::graph::DynamicGraph>(edges_per_vertex, feature_space, chunk_size);
|
|
66
|
+
return DynamicExplorationGraph(std::move(graph));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a random exploration graph from the given feature data (backed by SizeBoundedGraph).
|
|
71
|
+
*
|
|
72
|
+
* @param feature_data Pointer to a contiguous array of feature vectors.
|
|
73
|
+
* Each vector is feature_space.get_data_size() bytes.
|
|
74
|
+
* @param vertex_count Number of vertices to insert.
|
|
75
|
+
* @param edges_per_vertex Number of edges per vertex (must be even).
|
|
76
|
+
* @param feature_space The feature space defining dimensionality and metric.
|
|
77
|
+
* @param seed Random seed for deterministic graph construction.
|
|
78
|
+
* @return A new DynamicExplorationGraph wrapping the created random graph.
|
|
79
|
+
*/
|
|
80
|
+
inline DynamicExplorationGraph create_random_graph(
|
|
81
|
+
const std::byte* feature_data,
|
|
82
|
+
const uint32_t vertex_count,
|
|
83
|
+
const uint8_t edges_per_vertex,
|
|
84
|
+
const deglib::distances::FloatSpace& feature_space,
|
|
85
|
+
const uint32_t seed = 7
|
|
86
|
+
) {
|
|
87
|
+
auto graph = std::make_unique<deglib::graph::SizeBoundedGraph>(vertex_count, edges_per_vertex, feature_space);
|
|
88
|
+
deglib::builder::populate_random_graph(*graph, feature_data, vertex_count, seed);
|
|
89
|
+
return DynamicExplorationGraph(std::move(graph));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Load a saved graph from disk as a compact, immutable DynamicExplorationGraph (backed by ReadOnlyGraph).
|
|
94
|
+
*/
|
|
95
|
+
inline DynamicExplorationGraph load_readonly_graph(const char* path_graph) {
|
|
96
|
+
auto graph = std::make_unique<deglib::graph::ReadOnlyGraph>(deglib::graph::load_readonly_graph(path_graph));
|
|
97
|
+
return DynamicExplorationGraph(std::move(graph));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Load a saved graph from disk as a chunk-allocated mutable DynamicExplorationGraph (backed by DynamicGraph).
|
|
102
|
+
*/
|
|
103
|
+
inline DynamicExplorationGraph load_dynamic_graph(const char* path_graph, const uint32_t chunk_size = 1024) {
|
|
104
|
+
auto graph = std::make_unique<deglib::graph::DynamicGraph>(deglib::graph::load_dynamic_graph(path_graph, chunk_size));
|
|
105
|
+
return DynamicExplorationGraph(std::move(graph));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Load a saved graph from disk as a fixed-capacity mutable DynamicExplorationGraph (backed by SizeBoundedGraph).
|
|
110
|
+
*
|
|
111
|
+
* @param new_max_size Optional capacity override. If 0, uses the graph's serialized size.
|
|
112
|
+
*/
|
|
113
|
+
inline DynamicExplorationGraph load_mutable_graph(const char* path_graph, const uint32_t new_max_size = 0) {
|
|
114
|
+
auto graph = std::make_unique<deglib::graph::SizeBoundedGraph>(deglib::graph::load_sizebounded_graph(path_graph, new_max_size));
|
|
115
|
+
return DynamicExplorationGraph(std::move(graph));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
} // namespace deglib
|
|
@@ -110,8 +110,26 @@ class InnerProductFP16_AVX512 {
|
|
|
110
110
|
__m512 sum512 = _mm512_add_ps(sum512_1, sum512_2);
|
|
111
111
|
float result = fp16_hsum512(sum512);
|
|
112
112
|
|
|
113
|
-
//
|
|
113
|
+
// Vectorized residual tail for arbitrary dimensions
|
|
114
114
|
if constexpr (HasTail) {
|
|
115
|
+
if ((last - a) >= 8) {
|
|
116
|
+
__m256 va8 = _mm256_cvtph_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(a)));
|
|
117
|
+
__m256 vb8 = _mm256_cvtph_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(b)));
|
|
118
|
+
__m256 prod8 = _mm256_mul_ps(va8, vb8);
|
|
119
|
+
result += fp16_hsum256(prod8);
|
|
120
|
+
a += 8;
|
|
121
|
+
b += 8;
|
|
122
|
+
}
|
|
123
|
+
if ((last - a) >= 4) {
|
|
124
|
+
__m128 va4 = _mm_cvtph_ps(_mm_loadu_si64(a));
|
|
125
|
+
__m128 vb4 = _mm_cvtph_ps(_mm_loadu_si64(b));
|
|
126
|
+
__m128 prod4 = _mm_mul_ps(va4, vb4);
|
|
127
|
+
prod4 = _mm_add_ps(prod4, _mm_movehl_ps(prod4, prod4));
|
|
128
|
+
prod4 = _mm_add_ss(prod4, _mm_shuffle_ps(prod4, prod4, 1));
|
|
129
|
+
result += _mm_cvtss_f32(prod4);
|
|
130
|
+
a += 4;
|
|
131
|
+
b += 4;
|
|
132
|
+
}
|
|
115
133
|
while (a < last) {
|
|
116
134
|
float fa = deglib::distances::fp16::fp16_to_float(*a++);
|
|
117
135
|
float fb = deglib::distances::fp16::fp16_to_float(*b++);
|
|
@@ -243,8 +261,18 @@ class InnerProductFP16_AVX2 {
|
|
|
243
261
|
__m256 sum256 = _mm256_add_ps(sum256_1, sum256_2);
|
|
244
262
|
float result = fp16_hsum256(sum256);
|
|
245
263
|
|
|
246
|
-
//
|
|
264
|
+
// Vectorized residual tail for arbitrary dimensions
|
|
247
265
|
if constexpr (HasTail) {
|
|
266
|
+
if ((last - a) >= 4) {
|
|
267
|
+
__m128 va4 = _mm_cvtph_ps(_mm_loadu_si64(a));
|
|
268
|
+
__m128 vb4 = _mm_cvtph_ps(_mm_loadu_si64(b));
|
|
269
|
+
__m128 prod4 = _mm_mul_ps(va4, vb4);
|
|
270
|
+
prod4 = _mm_add_ps(prod4, _mm_movehl_ps(prod4, prod4));
|
|
271
|
+
prod4 = _mm_add_ss(prod4, _mm_shuffle_ps(prod4, prod4, 1));
|
|
272
|
+
result += _mm_cvtss_f32(prod4);
|
|
273
|
+
a += 4;
|
|
274
|
+
b += 4;
|
|
275
|
+
}
|
|
248
276
|
while (a < last) {
|
|
249
277
|
float fa = deglib::distances::fp16::fp16_to_float(*a++);
|
|
250
278
|
float fb = deglib::distances::fp16::fp16_to_float(*b++);
|
|
@@ -115,7 +115,27 @@ class L2FP16_AVX512 {
|
|
|
115
115
|
float result = fp16_l2_hsum512(sum512);
|
|
116
116
|
|
|
117
117
|
// Scalar residual for the unaligned tail — eliminated at compile-time if HasTail == false
|
|
118
|
+
// Vectorized residual tail for arbitrary dimensions
|
|
118
119
|
if constexpr (HasTail) {
|
|
120
|
+
if ((last - a) >= 8) {
|
|
121
|
+
__m256 va8 = _mm256_cvtph_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(a)));
|
|
122
|
+
__m256 vb8 = _mm256_cvtph_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(b)));
|
|
123
|
+
__m256 diff8 = _mm256_sub_ps(va8, vb8);
|
|
124
|
+
result += fp16_l2_hsum256(_mm256_mul_ps(diff8, diff8));
|
|
125
|
+
a += 8;
|
|
126
|
+
b += 8;
|
|
127
|
+
}
|
|
128
|
+
if ((last - a) >= 4) {
|
|
129
|
+
__m128 va4 = _mm_cvtph_ps(_mm_loadu_si64(a));
|
|
130
|
+
__m128 vb4 = _mm_cvtph_ps(_mm_loadu_si64(b));
|
|
131
|
+
__m128 diff4 = _mm_sub_ps(va4, vb4);
|
|
132
|
+
__m128 prod4 = _mm_mul_ps(diff4, diff4);
|
|
133
|
+
prod4 = _mm_add_ps(prod4, _mm_movehl_ps(prod4, prod4));
|
|
134
|
+
prod4 = _mm_add_ss(prod4, _mm_shuffle_ps(prod4, prod4, 1));
|
|
135
|
+
result += _mm_cvtss_f32(prod4);
|
|
136
|
+
a += 4;
|
|
137
|
+
b += 4;
|
|
138
|
+
}
|
|
119
139
|
while (a < last) {
|
|
120
140
|
float fa = deglib::distances::fp16::fp16_to_float(*a++);
|
|
121
141
|
float fb = deglib::distances::fp16::fp16_to_float(*b++);
|
|
@@ -251,8 +271,19 @@ class L2FP16_AVX2 {
|
|
|
251
271
|
__m256 sum256 = _mm256_add_ps(sum256_1, sum256_2);
|
|
252
272
|
float result = fp16_l2_hsum256(sum256);
|
|
253
273
|
|
|
254
|
-
//
|
|
274
|
+
// Vectorized residual tail for arbitrary dimensions
|
|
255
275
|
if constexpr (HasTail) {
|
|
276
|
+
if ((last - a) >= 4) {
|
|
277
|
+
__m128 va4 = _mm_cvtph_ps(_mm_loadu_si64(a));
|
|
278
|
+
__m128 vb4 = _mm_cvtph_ps(_mm_loadu_si64(b));
|
|
279
|
+
__m128 diff4 = _mm_sub_ps(va4, vb4);
|
|
280
|
+
__m128 prod4 = _mm_mul_ps(diff4, diff4);
|
|
281
|
+
prod4 = _mm_add_ps(prod4, _mm_movehl_ps(prod4, prod4));
|
|
282
|
+
prod4 = _mm_add_ss(prod4, _mm_shuffle_ps(prod4, prod4, 1));
|
|
283
|
+
result += _mm_cvtss_f32(prod4);
|
|
284
|
+
a += 4;
|
|
285
|
+
b += 4;
|
|
286
|
+
}
|
|
256
287
|
while (a < last) {
|
|
257
288
|
float fa = deglib::distances::fp16::fp16_to_float(*a++);
|
|
258
289
|
float fb = deglib::distances::fp16::fp16_to_float(*b++);
|
|
@@ -120,8 +120,29 @@ class InnerProductFloat_AVX512 {
|
|
|
120
120
|
_mm_store_ps(f, sum128);
|
|
121
121
|
float result = f[0] + f[1] + f[2] + f[3];
|
|
122
122
|
|
|
123
|
-
//
|
|
123
|
+
// Vectorized residual tail for arbitrary dimensions
|
|
124
124
|
if constexpr (HasTail) {
|
|
125
|
+
if ((last - a) >= 8) {
|
|
126
|
+
__m256 va8 = _mm256_loadu_ps(a);
|
|
127
|
+
__m256 vb8 = _mm256_loadu_ps(b);
|
|
128
|
+
__m256 prod8 = _mm256_mul_ps(va8, vb8);
|
|
129
|
+
__m128 sum128 = _mm_add_ps(_mm256_castps256_ps128(prod8), _mm256_extractf128_ps(prod8, 1));
|
|
130
|
+
alignas(16) float f[4];
|
|
131
|
+
_mm_store_ps(f, sum128);
|
|
132
|
+
result += f[0] + f[1] + f[2] + f[3];
|
|
133
|
+
a += 8;
|
|
134
|
+
b += 8;
|
|
135
|
+
}
|
|
136
|
+
if ((last - a) >= 4) {
|
|
137
|
+
__m128 va4 = _mm_loadu_ps(a);
|
|
138
|
+
__m128 vb4 = _mm_loadu_ps(b);
|
|
139
|
+
__m128 prod4 = _mm_mul_ps(va4, vb4);
|
|
140
|
+
prod4 = _mm_add_ps(prod4, _mm_movehl_ps(prod4, prod4));
|
|
141
|
+
prod4 = _mm_add_ss(prod4, _mm_shuffle_ps(prod4, prod4, 1));
|
|
142
|
+
result += _mm_cvtss_f32(prod4);
|
|
143
|
+
a += 4;
|
|
144
|
+
b += 4;
|
|
145
|
+
}
|
|
125
146
|
while (a < last) {
|
|
126
147
|
result = std::fma(*a++, *b++, result);
|
|
127
148
|
}
|
|
@@ -245,8 +266,18 @@ class InnerProductFloat_AVX2 {
|
|
|
245
266
|
__m256 sum256 = _mm256_add_ps(sum256_1, sum256_2);
|
|
246
267
|
float result = fp32_hsum256(sum256);
|
|
247
268
|
|
|
248
|
-
//
|
|
269
|
+
// Vectorized residual tail for arbitrary dimensions
|
|
249
270
|
if constexpr (HasTail) {
|
|
271
|
+
if ((last - a) >= 4) {
|
|
272
|
+
__m128 va4 = _mm_loadu_ps(a);
|
|
273
|
+
__m128 vb4 = _mm_loadu_ps(b);
|
|
274
|
+
__m128 prod4 = _mm_mul_ps(va4, vb4);
|
|
275
|
+
prod4 = _mm_add_ps(prod4, _mm_movehl_ps(prod4, prod4));
|
|
276
|
+
prod4 = _mm_add_ss(prod4, _mm_shuffle_ps(prod4, prod4, 1));
|
|
277
|
+
result += _mm_cvtss_f32(prod4);
|
|
278
|
+
a += 4;
|
|
279
|
+
b += 4;
|
|
280
|
+
}
|
|
250
281
|
while (a < last) {
|
|
251
282
|
result = std::fma(*a++, *b++, result);
|
|
252
283
|
}
|
|
@@ -118,8 +118,27 @@ class L2Float_AVX512 {
|
|
|
118
118
|
_mm_store_ps(f, sum128);
|
|
119
119
|
float result = f[0] + f[1] + f[2] + f[3];
|
|
120
120
|
|
|
121
|
-
//
|
|
121
|
+
// Vectorized residual tail for arbitrary dimensions
|
|
122
122
|
if constexpr (HasTail) {
|
|
123
|
+
if ((last - a) >= 8) {
|
|
124
|
+
__m256 va8 = _mm256_loadu_ps(a);
|
|
125
|
+
__m256 vb8 = _mm256_loadu_ps(b);
|
|
126
|
+
__m256 diff8 = _mm256_sub_ps(va8, vb8);
|
|
127
|
+
result += fp32_l2_hsum256(_mm256_mul_ps(diff8, diff8));
|
|
128
|
+
a += 8;
|
|
129
|
+
b += 8;
|
|
130
|
+
}
|
|
131
|
+
if ((last - a) >= 4) {
|
|
132
|
+
__m128 va4 = _mm_loadu_ps(a);
|
|
133
|
+
__m128 vb4 = _mm_loadu_ps(b);
|
|
134
|
+
__m128 diff4 = _mm_sub_ps(va4, vb4);
|
|
135
|
+
__m128 prod4 = _mm_mul_ps(diff4, diff4);
|
|
136
|
+
prod4 = _mm_add_ps(prod4, _mm_movehl_ps(prod4, prod4));
|
|
137
|
+
prod4 = _mm_add_ss(prod4, _mm_shuffle_ps(prod4, prod4, 1));
|
|
138
|
+
result += _mm_cvtss_f32(prod4);
|
|
139
|
+
a += 4;
|
|
140
|
+
b += 4;
|
|
141
|
+
}
|
|
123
142
|
while (a < last) {
|
|
124
143
|
float diff = *a++ - *b++;
|
|
125
144
|
result = std::fma(diff, diff, result);
|
|
@@ -243,8 +262,19 @@ class L2Float_AVX2 {
|
|
|
243
262
|
__m256 sum256 = _mm256_add_ps(sum256_1, sum256_2);
|
|
244
263
|
float result = fp32_l2_hsum256(sum256);
|
|
245
264
|
|
|
246
|
-
//
|
|
265
|
+
// Vectorized residual tail for arbitrary dimensions
|
|
247
266
|
if constexpr (HasTail) {
|
|
267
|
+
if ((last - a) >= 4) {
|
|
268
|
+
__m128 va4 = _mm_loadu_ps(a);
|
|
269
|
+
__m128 vb4 = _mm_loadu_ps(b);
|
|
270
|
+
__m128 diff4 = _mm_sub_ps(va4, vb4);
|
|
271
|
+
__m128 prod4 = _mm_mul_ps(diff4, diff4);
|
|
272
|
+
prod4 = _mm_add_ps(prod4, _mm_movehl_ps(prod4, prod4));
|
|
273
|
+
prod4 = _mm_add_ss(prod4, _mm_shuffle_ps(prod4, prod4, 1));
|
|
274
|
+
result += _mm_cvtss_f32(prod4);
|
|
275
|
+
a += 4;
|
|
276
|
+
b += 4;
|
|
277
|
+
}
|
|
248
278
|
while (a < last) {
|
|
249
279
|
float diff = *a++ - *b++;
|
|
250
280
|
result = std::fma(diff, diff, result);
|
|
@@ -176,6 +176,34 @@ class InnerProductInt8_AVX512_VNNI {
|
|
|
176
176
|
int64_t result = int8_ip_hsum512(sum512) - (int8_ip_hsum512(q_comp) * 128);
|
|
177
177
|
|
|
178
178
|
if constexpr (HasTail) {
|
|
179
|
+
const size_t rem = last - a;
|
|
180
|
+
if (rem >= 32) {
|
|
181
|
+
__m256i raw_a256 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(a));
|
|
182
|
+
__m256i raw_b256 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(b));
|
|
183
|
+
__m256i prod1 = _mm256_madd_epi16(_mm256_cvtepi8_epi16(_mm256_castsi256_si128(raw_a256)), _mm256_cvtepi8_epi16(_mm256_castsi256_si128(raw_b256)));
|
|
184
|
+
__m256i prod2 = _mm256_madd_epi16(_mm256_cvtepi8_epi16(_mm256_extracti128_si256(raw_a256, 1)), _mm256_cvtepi8_epi16(_mm256_extracti128_si256(raw_b256, 1)));
|
|
185
|
+
result += int8_ip_hsum256(_mm256_add_epi32(prod1, prod2));
|
|
186
|
+
a += 32;
|
|
187
|
+
b += 32;
|
|
188
|
+
}
|
|
189
|
+
if ((last - a) >= 16) {
|
|
190
|
+
__m128i raw_a128 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(a));
|
|
191
|
+
__m128i raw_b128 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b));
|
|
192
|
+
__m256i prod = _mm256_madd_epi16(_mm256_cvtepi8_epi16(raw_a128), _mm256_cvtepi8_epi16(raw_b128));
|
|
193
|
+
result += int8_ip_hsum256(prod);
|
|
194
|
+
a += 16;
|
|
195
|
+
b += 16;
|
|
196
|
+
}
|
|
197
|
+
if ((last - a) >= 8) {
|
|
198
|
+
__m128i raw_a64 = _mm_loadu_si64(a);
|
|
199
|
+
__m128i raw_b64 = _mm_loadu_si64(b);
|
|
200
|
+
__m128i prod = _mm_madd_epi16(_mm_cvtepi8_epi16(raw_a64), _mm_cvtepi8_epi16(raw_b64));
|
|
201
|
+
prod = _mm_add_epi32(prod, _mm_shuffle_epi32(prod, _MM_SHUFFLE(1, 0, 3, 2)));
|
|
202
|
+
prod = _mm_add_epi32(prod, _mm_shuffle_epi32(prod, _MM_SHUFFLE(2, 3, 0, 1)));
|
|
203
|
+
result += static_cast<int64_t>(_mm_cvtsi128_si32(prod));
|
|
204
|
+
a += 8;
|
|
205
|
+
b += 8;
|
|
206
|
+
}
|
|
179
207
|
while (a < last) {
|
|
180
208
|
result += int64_t(*a++) * int64_t(*b++);
|
|
181
209
|
}
|
|
@@ -291,6 +319,34 @@ class InnerProductInt8_AVX512 {
|
|
|
291
319
|
int64_t result = int8_ip_hsum512(sum512);
|
|
292
320
|
|
|
293
321
|
if constexpr (HasTail) {
|
|
322
|
+
const size_t rem = last - a;
|
|
323
|
+
if (rem >= 32) {
|
|
324
|
+
__m256i raw_a256 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(a));
|
|
325
|
+
__m256i raw_b256 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(b));
|
|
326
|
+
__m256i prod1 = _mm256_madd_epi16(_mm256_cvtepi8_epi16(_mm256_castsi256_si128(raw_a256)), _mm256_cvtepi8_epi16(_mm256_castsi256_si128(raw_b256)));
|
|
327
|
+
__m256i prod2 = _mm256_madd_epi16(_mm256_cvtepi8_epi16(_mm256_extracti128_si256(raw_a256, 1)), _mm256_cvtepi8_epi16(_mm256_extracti128_si256(raw_b256, 1)));
|
|
328
|
+
result += int8_ip_hsum256(_mm256_add_epi32(prod1, prod2));
|
|
329
|
+
a += 32;
|
|
330
|
+
b += 32;
|
|
331
|
+
}
|
|
332
|
+
if ((last - a) >= 16) {
|
|
333
|
+
__m128i raw_a128 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(a));
|
|
334
|
+
__m128i raw_b128 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b));
|
|
335
|
+
__m256i prod = _mm256_madd_epi16(_mm256_cvtepi8_epi16(raw_a128), _mm256_cvtepi8_epi16(raw_b128));
|
|
336
|
+
result += int8_ip_hsum256(prod);
|
|
337
|
+
a += 16;
|
|
338
|
+
b += 16;
|
|
339
|
+
}
|
|
340
|
+
if ((last - a) >= 8) {
|
|
341
|
+
__m128i raw_a64 = _mm_loadu_si64(a);
|
|
342
|
+
__m128i raw_b64 = _mm_loadu_si64(b);
|
|
343
|
+
__m128i prod = _mm_madd_epi16(_mm_cvtepi8_epi16(raw_a64), _mm_cvtepi8_epi16(raw_b64));
|
|
344
|
+
prod = _mm_add_epi32(prod, _mm_shuffle_epi32(prod, _MM_SHUFFLE(1, 0, 3, 2)));
|
|
345
|
+
prod = _mm_add_epi32(prod, _mm_shuffle_epi32(prod, _MM_SHUFFLE(2, 3, 0, 1)));
|
|
346
|
+
result += static_cast<int64_t>(_mm_cvtsi128_si32(prod));
|
|
347
|
+
a += 8;
|
|
348
|
+
b += 8;
|
|
349
|
+
}
|
|
294
350
|
while (a < last) {
|
|
295
351
|
result += int64_t(*a++) * int64_t(*b++);
|
|
296
352
|
}
|
|
@@ -410,6 +466,16 @@ class InnerProductInt8_AVX2_VNNI {
|
|
|
410
466
|
int64_t result = int8_ip_hsum256(sum256) - (int8_ip_hsum256(q_comp) * 128);
|
|
411
467
|
|
|
412
468
|
if constexpr (HasTail) {
|
|
469
|
+
if ((last - a) >= 8) {
|
|
470
|
+
__m128i raw_a64 = _mm_loadu_si64(a);
|
|
471
|
+
__m128i raw_b64 = _mm_loadu_si64(b);
|
|
472
|
+
__m128i prod = _mm_madd_epi16(_mm_cvtepi8_epi16(raw_a64), _mm_cvtepi8_epi16(raw_b64));
|
|
473
|
+
prod = _mm_add_epi32(prod, _mm_shuffle_epi32(prod, _MM_SHUFFLE(1, 0, 3, 2)));
|
|
474
|
+
prod = _mm_add_epi32(prod, _mm_shuffle_epi32(prod, _MM_SHUFFLE(2, 3, 0, 1)));
|
|
475
|
+
result += static_cast<int64_t>(_mm_cvtsi128_si32(prod));
|
|
476
|
+
a += 8;
|
|
477
|
+
b += 8;
|
|
478
|
+
}
|
|
413
479
|
while (a < last) {
|
|
414
480
|
result += int64_t(*a++) * int64_t(*b++);
|
|
415
481
|
}
|
|
@@ -524,6 +590,16 @@ class InnerProductInt8_AVX2 {
|
|
|
524
590
|
int64_t result = int8_ip_hsum256(sum256);
|
|
525
591
|
|
|
526
592
|
if constexpr (HasTail) {
|
|
593
|
+
if ((last - a) >= 8) {
|
|
594
|
+
__m128i raw_a64 = _mm_loadu_si64(a);
|
|
595
|
+
__m128i raw_b64 = _mm_loadu_si64(b);
|
|
596
|
+
__m128i prod = _mm_madd_epi16(_mm_cvtepi8_epi16(raw_a64), _mm_cvtepi8_epi16(raw_b64));
|
|
597
|
+
prod = _mm_add_epi32(prod, _mm_shuffle_epi32(prod, _MM_SHUFFLE(1, 0, 3, 2)));
|
|
598
|
+
prod = _mm_add_epi32(prod, _mm_shuffle_epi32(prod, _MM_SHUFFLE(2, 3, 0, 1)));
|
|
599
|
+
result += static_cast<int64_t>(_mm_cvtsi128_si32(prod));
|
|
600
|
+
a += 8;
|
|
601
|
+
b += 8;
|
|
602
|
+
}
|
|
527
603
|
while (a < last) {
|
|
528
604
|
result += int64_t(*a++) * int64_t(*b++);
|
|
529
605
|
}
|