Skip to content

Commit cee39ff

Browse files
committed
refactor(docs, python, cpp): modernize docs with uv, streamline graph factory API, and replace runtime asserts
- Modernize docs directory: migrate to uv + pyproject.toml, remove legacy make/sh/bat scripts, dynamic version from deglib.__version__ - Complete Sphinx API reference across all 7 Python modules (graph, builder, distances, search, analysis, optimization, cpu) - Expose load_mutable_graph and streamline graph creation/loading as top-level helpers (remove redundant classmethods) - Eliminate internal to_cpp methods from public Python API and remove unused std.py module - Replace runtime assertions with proper exceptions (ValueError/std::runtime_error) in builder.py and repository.h
1 parent d24e41e commit cee39ff

57 files changed

Lines changed: 2581 additions & 839 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.readthedocs.yaml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ build:
77

88
python:
99
install:
10-
- requirements: docs/requirements.txt
10+
- method: pip
11+
path: docs
1112

1213
sphinx:
1314
configuration: docs/conf.py

‎cpp/bench/include/repository.h‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#pragma once
22

3-
#include <assert.h>
43
#include <stdio.h>
54

65
#include <cstring>
76
#include <filesystem>
87
#include <fstream>
98
#include <iostream>
9+
#include <stdexcept>
1010
#include <unordered_map>
1111

1212
namespace deglib {
@@ -73,10 +73,14 @@ auto fvecs_read(const char* fname, size_t& d_out, size_t& n_out) {
7373
// read dimension header
7474
uint32_t dims = 0;
7575
ifstream.read(reinterpret_cast<char*>(&dims), sizeof(dims));
76-
assert((dims > 0 && dims < 1'000'000) && "unreasonable dimension");
76+
if (dims == 0 || dims >= 1'000'000) {
77+
throw std::runtime_error("unreasonable dimension in fvecs file");
78+
}
7779

7880
// compute number of rows
79-
assert(file_size % ((dims + 1) * sizeof(float)) == 0 || !"weird file size");
81+
if (file_size % ((dims + 1) * sizeof(float)) != 0) {
82+
throw std::runtime_error("weird file size in fvecs file");
83+
}
8084
size_t n = (size_t)file_size / ((dims + 1) * sizeof(float));
8185
d_out = dims;
8286
n_out = n;
@@ -85,7 +89,9 @@ auto fvecs_read(const char* fname, size_t& d_out, size_t& n_out) {
8589
auto x = std::make_unique<std::byte[]>(file_size);
8690
ifstream.seekg(0);
8791
ifstream.read(reinterpret_cast<char*>(x.get()), file_size);
88-
if (!ifstream) assert(ifstream.gcount() == static_cast<int>(file_size) || !"could not read whole file");
92+
if (!ifstream && ifstream.gcount() != static_cast<std::streamsize>(file_size)) {
93+
throw std::runtime_error("could not read whole file");
94+
}
8995

9096
// shift array to remove row headers
9197
for (size_t i = 0; i < n; i++) std::memmove(&x[i * dims * sizeof(float)], &x[sizeof(dims) + i * (dims + 1) * sizeof(float)], dims * sizeof(float));
@@ -113,10 +119,14 @@ auto u8vecs_read(const char* fname, size_t& d_out, size_t& n_out) {
113119
// read dimension header
114120
uint32_t dims = 0;
115121
ifstream.read(reinterpret_cast<char*>(&dims), sizeof(dims));
116-
assert((dims > 0 && dims < 1'000'000) && "unreasonable dimension");
122+
if (dims == 0 || dims >= 1'000'000) {
123+
throw std::runtime_error("unreasonable dimension in u8vecs file");
124+
}
117125

118126
// compute number of rows
119-
assert(file_size % (dims + 4) == 0 || !"weird file size");
127+
if (file_size % (dims + 4) != 0) {
128+
throw std::runtime_error("weird file size in u8vecs file");
129+
}
120130
size_t n = (size_t)file_size / (dims + 4);
121131
d_out = dims;
122132
n_out = n;
@@ -125,7 +135,9 @@ auto u8vecs_read(const char* fname, size_t& d_out, size_t& n_out) {
125135
auto x = std::make_unique<std::byte[]>(file_size);
126136
ifstream.seekg(0);
127137
ifstream.read(reinterpret_cast<char*>(x.get()), file_size);
128-
if (!ifstream) assert(ifstream.gcount() == static_cast<int>(file_size) || !"could not read whole file");
138+
if (!ifstream && ifstream.gcount() != static_cast<std::streamsize>(file_size)) {
139+
throw std::runtime_error("could not read whole file");
140+
}
129141

130142
// shift array to remove row headers
131143
for (size_t i = 0; i < n; i++) std::memmove(&x[i * dims], &x[sizeof(dims) + i * (dims + sizeof(dims))], dims);

‎cpp/deglib/include/deglib/builder.h‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -398,9 +398,10 @@ class EvenRegularGraphBuilder {
398398
* Set the number of threads used to extend the graph during building.
399399
*
400400
* When the thread count is greater than 1 and the optimization target is not StreamingData,
401-
* the builder will utilize multiple threads to add elements to the graph in parallel.
402-
* By default, all available CPU cores/threads are used unless specified.
403-
* Note: The order in which elements are added is not guaranteed when using multiple threads.
401+
* the builder will utilize multiple threads to search neighbors and connect edges in parallel.
402+
* The default thread count is 1.
403+
* Note: Elements are always appended to the graph in insertion order. Only the neighbor
404+
* search and edge creation of the elements within a batch are processed in parallel.
404405
*
405406
* @param thread_count Number of threads which are used to extend the graph.
406407
*/
@@ -417,11 +418,14 @@ class EvenRegularGraphBuilder {
417418
* (how fast changed are added to the graph), specify the batch size should be considered.
418419
*
419420
* e.g.
420-
* thread count = 1 and batch size = 1: low throuput, medium latency, order of elements is guaranteed
421-
* thread count > 1 and batch size = 1: high throuput, low latency, order of elements is not guaranteed
422-
* thread count > 1 and batch size > 1: highest throuput, highest latency, order of elements is not guaranteed
421+
* thread count = 1 and batch size = 1: low throughput, medium latency
422+
* thread count > 1 and batch size = 1: high throughput, low latency
423+
* thread count > 1 and batch size > 1: highest throughput, highest latency
423424
* * Please note that the optimization target StreamingData only uses a thread count of 1.
424425
*
426+
* The elements of a batch are always appended to the graph in insertion order.
427+
* Only the neighbor search and edge creation within a batch are processed in parallel.
428+
*
425429
* The batch size is calculated as:
426430
* batch_size = thread_count * tasks_per_batch * task_size
427431
* where
@@ -432,8 +436,8 @@ class EvenRegularGraphBuilder {
432436
* A low tasks_per_batch improves the latency but reduces the throughput.
433437
* Therefore it is recommended to use a higher task_size value.
434438
*
435-
* @param tasks_per_batch Number of tasks for each thread in one batch. (default: 32)
436-
* @param task_size Number of elements each thread processes in one task. (default: 10)
439+
* @param tasks_per_batch Number of tasks for each thread in one batch. (default: 10)
440+
* @param task_size Number of elements each thread processes in one task. (default: 32)
437441
*/
438442
void setBatchSize(uint32_t tasks_per_batch, uint32_t task_size) {
439443
extend_thread_task_size = task_size;

‎cpp/deglib/include/deglib/optimization.h‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ inline void prune_worst_edges(deglib::graph::MutableGraph& graph, const uint8_t
109109
}
110110

111111
/**
112-
* @brief Remove all edges that do not satisfy the MRNG condition.
112+
* @brief Remove all edges that do not satisfy the RNG condition.
113113
*
114114
* Parallelized across hardware threads. For each vertex, checks each neighbor
115115
* using the RNG condition and removes non-conforming edges.
@@ -118,12 +118,12 @@ inline void prune_worst_edges(deglib::graph::MutableGraph& graph, const uint8_t
118118
* @param numThreads Number of threads to use (0 = use hardware concurrency).
119119
* @return Number of edges removed.
120120
*/
121-
inline uint32_t prune_non_mrng_edges(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
122-
return deglib::optimization::pruning::prune_non_mrng_edges(graph, numThreads);
121+
inline uint32_t prune_non_rng_edges(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
122+
return deglib::optimization::pruning::prune_non_rng_edges(graph, numThreads);
123123
}
124124

125125
/**
126-
* @brief Remove non-MRNG edges using a weight-sorted global strategy.
126+
* @brief Remove non-RNG edges using a weight-sorted global strategy.
127127
*
128128
* Collects all non-RNG edges, sorts them by weight (ascending), then removes
129129
* them in that order. Collection is multi-threaded; removal is single-threaded.
@@ -132,12 +132,12 @@ inline uint32_t prune_non_mrng_edges(deglib::graph::MutableGraph& graph, const s
132132
* @param numThreads Number of threads to use for collection (0 = use hardware concurrency).
133133
* @return Number of edges removed.
134134
*/
135-
inline uint32_t prune_non_mrng_edges_weight_sorted(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
136-
return deglib::optimization::pruning::prune_non_mrng_edges_weight_sorted(graph, numThreads);
135+
inline uint32_t prune_non_rng_edges_weight_sorted(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
136+
return deglib::optimization::pruning::prune_non_rng_edges_weight_sorted(graph, numThreads);
137137
}
138138

139139
/**
140-
* @brief Remove non-MRNG edges using an iterative per-vertex strategy.
140+
* @brief Remove non-RNG edges using an iterative per-vertex strategy.
141141
*
142142
* For each vertex, iteratively removes non-RNG edges in a do-while loop until
143143
* no more edges can be removed. This accounts for cascading effects where
@@ -147,8 +147,8 @@ inline uint32_t prune_non_mrng_edges_weight_sorted(deglib::graph::MutableGraph&
147147
* @param numThreads Number of threads to use (0 = use hardware concurrency).
148148
* @return Number of edges removed.
149149
*/
150-
inline uint32_t prune_non_mrng_edges_iterative(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
151-
return deglib::optimization::pruning::prune_non_mrng_edges_iterative(graph, numThreads);
150+
inline uint32_t prune_non_rng_edges_iterative(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
151+
return deglib::optimization::pruning::prune_non_rng_edges_iterative(graph, numThreads);
152152
}
153153

154154
// ========================================================================

‎cpp/deglib/include/deglib/optimization/pruning.h‎

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ inline void prune_worst_edges(deglib::graph::MutableGraph& graph, const uint8_t
6868
}
6969

7070
/**
71-
* @brief Remove all edges that do not satisfy the MRNG condition.
71+
* @brief Remove all edges that do not satisfy the RNG condition.
7272
*
7373
* Iterates over all vertices and their neighbors, removing any edge that does
7474
* not satisfy the RNG condition. Each vertex is processed in parallel via
@@ -78,7 +78,7 @@ inline void prune_worst_edges(deglib::graph::MutableGraph& graph, const uint8_t
7878
* @param numThreads Number of threads to use (0 = use hardware concurrency).
7979
* @return Number of edges removed.
8080
*/
81-
inline uint32_t prune_non_mrng_edges(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
81+
inline uint32_t prune_non_rng_edges(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
8282
const auto vertex_count = graph.size();
8383
const auto edge_per_vertex = graph.getEdgesPerVertex();
8484
const auto thread_count = numThreads == 0 ? std::thread::hardware_concurrency() : numThreads;
@@ -117,7 +117,7 @@ inline uint32_t prune_non_mrng_edges(deglib::graph::MutableGraph& graph, const s
117117
}
118118

119119
/**
120-
* @brief Remove non-MRNG edges using a weight-sorted global strategy.
120+
* @brief Remove non-RNG edges using a weight-sorted global strategy.
121121
*
122122
* Collects all non-RNG edges across the graph, sorts them by weight (ascending),
123123
* then removes them in that order. This allows lower-weight (more important) edges
@@ -128,7 +128,7 @@ inline uint32_t prune_non_mrng_edges(deglib::graph::MutableGraph& graph, const s
128128
* @param numThreads Number of threads to use for collection (0 = use hardware concurrency).
129129
* @return Number of edges removed.
130130
*/
131-
inline uint32_t prune_non_mrng_edges_weight_sorted(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
131+
inline uint32_t prune_non_rng_edges_weight_sorted(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
132132
struct WeightedEdge {
133133
uint32_t from_vertex;
134134
uint32_t to_vertex;
@@ -140,7 +140,7 @@ inline uint32_t prune_non_mrng_edges_weight_sorted(deglib::graph::MutableGraph&
140140
const auto thread_count = numThreads == 0 ? std::thread::hardware_concurrency() : numThreads;
141141

142142
// Collect all non-RNG edges (multi-threaded, per-thread buffers to avoid data races)
143-
std::vector<std::vector<WeightedEdge>> nonMRNG_edges_per_thread(thread_count);
143+
std::vector<std::vector<WeightedEdge>> non_rng_edges_per_thread(thread_count);
144144
deglib::concurrent::parallel_for(0, vertex_count, thread_count, [&](size_t vertex_index, size_t thread_id) {
145145
const auto u = static_cast<uint32_t>(vertex_index);
146146
const auto neighbor_indices = graph.getNeighborIndices(u);
@@ -150,24 +150,24 @@ inline uint32_t prune_non_mrng_edges_weight_sorted(deglib::graph::MutableGraph&
150150
const auto neighbor_index = neighbor_indices[n];
151151
const auto neighbor_weight = neighbor_weights[n];
152152
if (deglib::analysis::checkRNG(graph, edge_per_vertex, u, neighbor_index, neighbor_weight) == false) {
153-
nonMRNG_edges_per_thread[thread_id].push_back({u, neighbor_index, neighbor_weight});
153+
non_rng_edges_per_thread[thread_id].push_back({u, neighbor_index, neighbor_weight});
154154
}
155155
}
156156
});
157157

158158
// Merge per-thread results
159-
std::vector<WeightedEdge> nonMRNG_edges;
159+
std::vector<WeightedEdge> non_rng_edges;
160160
for (size_t i = 0; i < thread_count; i++) {
161-
nonMRNG_edges.insert(nonMRNG_edges.end(), nonMRNG_edges_per_thread[i].begin(), nonMRNG_edges_per_thread[i].end());
161+
non_rng_edges.insert(non_rng_edges.end(), non_rng_edges_per_thread[i].begin(), non_rng_edges_per_thread[i].end());
162162
}
163163

164164
// Sort by weight ascending
165-
std::sort(nonMRNG_edges.begin(), nonMRNG_edges.end(), [](const auto& x, const auto& y) { return x.weight < y.weight; });
165+
std::sort(non_rng_edges.begin(), non_rng_edges.end(), [](const auto& x, const auto& y) { return x.weight < y.weight; });
166166

167167
// Remove edges that are still non-RNG (single-threaded)
168168
size_t removed_rng_edges = 0;
169-
for (size_t i = 0; i < nonMRNG_edges.size(); i++) {
170-
const auto& edge = nonMRNG_edges[i];
169+
for (size_t i = 0; i < non_rng_edges.size(); i++) {
170+
const auto& edge = non_rng_edges[i];
171171
if (deglib::analysis::checkRNG(graph, edge_per_vertex, edge.from_vertex, edge.to_vertex, edge.weight) == false) {
172172
graph.changeEdge(edge.from_vertex, edge.to_vertex, edge.from_vertex, 0);
173173
removed_rng_edges++;
@@ -178,7 +178,7 @@ inline uint32_t prune_non_mrng_edges_weight_sorted(deglib::graph::MutableGraph&
178178
}
179179

180180
/**
181-
* @brief Remove non-MRNG edges using an iterative per-vertex strategy.
181+
* @brief Remove non-RNG edges using an iterative per-vertex strategy.
182182
*
183183
* For each vertex, iteratively removes non-RNG edges in a do-while loop until
184184
* no more edges can be removed. This accounts for cascading effects where
@@ -189,7 +189,7 @@ inline uint32_t prune_non_mrng_edges_weight_sorted(deglib::graph::MutableGraph&
189189
* @param numThreads Number of threads to use (0 = use hardware concurrency).
190190
* @return Number of edges removed.
191191
*/
192-
inline uint32_t prune_non_mrng_edges_iterative(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
192+
inline uint32_t prune_non_rng_edges_iterative(deglib::graph::MutableGraph& graph, const size_t numThreads = 0) {
193193
const auto vertex_count = graph.size();
194194
const auto edge_per_vertex = graph.getEdgesPerVertex();
195195
const auto thread_count = numThreads == 0 ? std::thread::hardware_concurrency() : numThreads;
@@ -211,23 +211,23 @@ inline uint32_t prune_non_mrng_edges_iterative(deglib::graph::MutableGraph& grap
211211
}
212212

213213
// Find all non-RNG conform neighbors (indices into the sorted neighbors vector)
214-
std::vector<uint32_t> nonMRNG_edges;
214+
std::vector<uint32_t> non_rng_edges;
215215
for (uint32_t n = 0; n < neighbors.size(); n++) {
216216
const auto neighbor_index = neighbors[n].first;
217217
const auto neighbor_weight = neighbors[n].second;
218-
if (deglib::analysis::checkRNG(graph, edge_per_vertex, vertex_index_u32, neighbor_index, neighbor_weight) == false) nonMRNG_edges.emplace_back(n);
218+
if (deglib::analysis::checkRNG(graph, edge_per_vertex, vertex_index_u32, neighbor_index, neighbor_weight) == false) non_rng_edges.emplace_back(n);
219219
}
220220

221221
// Iteratively remove edges until stable
222222
bool removed_edge = false;
223223
do {
224224
removed_edge = false;
225-
for (uint32_t n = 0; n < nonMRNG_edges.size(); n++) {
226-
const auto neighbor_index = neighbors[nonMRNG_edges[n]].first;
227-
const auto neighbor_weight = neighbors[nonMRNG_edges[n]].second;
225+
for (uint32_t n = 0; n < non_rng_edges.size(); n++) {
226+
const auto neighbor_index = neighbors[non_rng_edges[n]].first;
227+
const auto neighbor_weight = neighbors[non_rng_edges[n]].second;
228228

229229
if (deglib::analysis::checkRNG(graph, edge_per_vertex, vertex_index_u32, neighbor_index, neighbor_weight) == false) {
230-
nonMRNG_edges.erase(nonMRNG_edges.begin() + n);
230+
non_rng_edges.erase(non_rng_edges.begin() + n);
231231
graph.changeEdge(vertex_index_u32, neighbor_index, vertex_index_u32, 0);
232232
removed_rng_edges++;
233233
removed_edge = true;

0 commit comments

Comments
 (0)