## FlatNav
FlatNav is a fast and header-only graph-based index for Approximate Nearest Neighbor Search (ANNS). FlatNav is inspired by the influential [Hierarchical Navigable Small World (HNSW) index](https://github.com/nmslib/hnswlib), but with the hierarchical component removed. As detailed in our [research paper](https://arxiv.org/pdf/2412.01940), we found that FlatNav achieved identical performance to HNSW on high-dimensional datasets (dimensionality > 32) with approximately 38% less peak memory consumption and a simplified implementation.
We hope to maintain this open source library as a resource for broader community. Please consider opening a Github Issue for bugs and feature requests, or get in touch with us directly for discussions.
### Installation
FlatNav is implemented in C++ with a complete Python extension with [cereal](https://uscilab.github.io/cereal/) as the only external dependency. This is a header-only library, so there is nothing to build. You can just include the necessary headers in your existing code.
FlatNav is supported on x86-64 machines on linux and MacOS (we can extend this to windows if there is sufficient interest). To get the C++ library working and run examples under the [tools](https://github.com/BlaiseMuhirwa/flatnav/blob/main/tools) directory, you will need
* C++17 compiler with OpenMP support (version >= 2.0)
* CMake (version >= 3.14)
We provide some helpful scripts for installing the above in the [bin](https://github.com/BlaiseMuhirwa/flatnav/tree/main/bin) directory.
To generate the library with CMake and compile examples, run
```shell
$ git clone https://github.com/BlaiseMuhirwa/flatnav.git --recurse-submodules
$ cd flatnav
$ ./bin/build.sh -e
```
You can get all options available with the `build.sh` script by passing it the `-h` argument.
This will display all available build options:
```shell
Usage ./build.sh [OPTIONS]
Available Options:
-t, --tests: Build tests
-e, --examples: Build examples
-v, --verbose: Make verbose
-b, --benchmark: Build benchmarks
-bt, --build_type: Build type (Debug, Release, RelWithDebInfo, MinSizeRel)
-nmv, --no_simd_vectorization:Disable SIMD instructions
-h, --help: Print this help message
Example Usage:
./build.sh -t -e -v
```
To build the Python bindings, follow instructions [here](https://github.com/BlaiseMuhirwa/flatnav/blob/main/flatnav_python/README.md). There are also examples for how to use the library to build an index and run queries on top of it [here](https://github.com/BlaiseMuhirwa/flatnav/blob/main/flatnav_python/unit_tests/test_index.py).
### Support for SIMD Extensions
We currently support SIMD extensions for certain platforms as detailed below.
| Operation | x86_64 | arm64v8 | Apple silicon |
|-----------|--------|---------|-----------------|
| FP32 Inner product |SSE, AVX, AVX512 | No SIMD support | No SIMD support |
| FP32 L2 distance |SSE, AVX, AVX512| No SIMD support | No SIMD support |
| UINT8 L2 distance |AVX512 | No SIMD support | No SIMD support |
| INT8 L2 distance | SSE | No SIMD support | No SIMD support |
### Getting Started in Python
Currently, we support Python wheels for versions 3.8 through 3.12 on x86_64 architectures (Intel, AMD and MacOS). Support for
ARM wheels is a future improvement.
The python library can be installed from PyPI by using
```shell
$ pip install flatnav
```
Similarly, `flatnav` can be installed from source via [cibuildwheel](https://cibuildwheel.pypa.io/en/stable/), which
builds cross-platform wheels. Follow the following steps
```shell
$ git clone https://github.com/BlaiseMuhirwa/flatnav.git --recurse-submodules
$ cd flatnav
$ make install-cibuildwheel
# This will build flatnav for the current version in your environment. If you want to build wheels
# for all supported python versions (3.8 to 3.12), remove the --current-version flag.
$ ./cibuild.sh --current-version
$ pip install wheelhouse/flatnav*.whl --force-reinstall
```
Once you have the python library installed and you have a dataset you want to index as a numpy array, you can construct the index as shown below. This will allocate memory and create a directed graph with vectors as nodes.
```python
import numpy as np
import flatnav
from flatnav.data_type import DataType
# Get your numpy-formatted dataset.
dataset_size = 1_000_000
dataset_dimension = 128
dataset_to_index = np.random.randn(dataset_size, dataset_dimension)
# Define index construction parameters.
distance_type = "l2"
max_edges_per_node = 32
ef_construction = 100
num_build_threads = 16
# Create index configuration and pre-allocate memory
index = flatnav.index.create(
distance_type=distance_type,
index_data_type=DataType.float32,
dim=dataset_dimension,
dataset_size=dataset_size,
max_edges_per_node=max_edges_per_node,
verbose=True,
collect_stats=True,
)
index.set_num_threads(num_build_threads)
# Now index the dataset
index.add(data=dataset_to_index, ef_construction=ef_construction)
```
Note that we specified `DataType.float32` to indicate that we want to build an index with vectors represented with `float` type. If you want to use a different precision, such as `uint8_t` or `int8_t` (which are the only other ones currently supported), you can use `DataType.uint8` or `DataType.int8`.
The distance type can either be `l2` or `angular`. The `collect_stats` flag will record the number of distance evaluations.
To query the index we just created by generating IID vectors from the standard normal distribution, we do it as follows
```python
# Set query-time parameters
k = 100
ef_search = 100
# Run k-NN query with a single thread.
index.set_num_threads(1)
queries = np.random.randn(1000, dataset_to_index.shape[1])
for query in queries:
distances, indices = index.search_single(
query=query,
ef_search=ef_search,
K=k,
)
```
You can parallelize the search by setting the number of threads to a desired number and using a different API that also returns the exact same results as `search_single`.
```python
index.set_num_threads(16)
distances, indices = index.search(queries=queries, ef_search=ef_search, K=k)
```
### Getting Started in C++
As mentioned earlier, there is nothing to build since this is header-only. We will translate the above Python code in C++ to illustrate how to use the C++ API.
```c++
#include <cstdint>
#include <flatnav/index/Index.h>
#include <flatnav/distances/SquaredL2Distance.h>
#include <flatnav/distances/DistanceInterface.h>
template <typename dist_t>
void run_knn_search(Index<dist_t, int>>* index, float *queries, int* gtruth,
int ef_search, int K, int num_queries, int num_gtruth, int dim) {
float mean_recall = 0;
for (int i = 0; i < num_queries; i++) {
float *q = queries + dim * i;
int *g = gtruth + num_gtruth * i;
std::vector<std::pair<float, int>> result =
index->search(q, K, ef_search);
float recall = 0;
for (int j = 0; j < K; j++) {
for (int l = 0; l < K; l++) {
if (result[j].second == g[l]) {
recall = recall + 1;
}
}
}
recall = recall / K;
mean_recall = mean_recall + recall;
}
}
int main(int argc, char** argv) {
uint32_t dataset_size = 1000000;
uint32_t dataset_dimension = 128;
// We skip the random data generation, but you can do that with std::mt19937, std::random_device
// and std::normal_distribution
// std::vector<float> dataset_to_index;
uint32_t max_edges_per_node = 32;
uint32_t ef_construction = 100;
// Create an index with l2 distance
auto distance = SquaredL2Distance<>::create(dataset_dimension);
auto* index = new Index<SquaredL2Distance<DataType::float32>>, int>(
/* dist = */ std::move(distance), /* dataset_size = */ dataset_size,
/* max_edges_per_node = */ max_edges_per_node);
index->setNumThreads(build_num_threads);
std::vector<int> labels(dataset_size);
std::iota(labels.begin(), labels.end(), 0);
index->template addBatch<float>(/* data = */ (void *)dataset_to_index,
/* labels = */ labels,
/* ef_construction */ ef_construction);
// Now query the index and compute the recall
// We assume you have a ground truth (int*) array and a queries (float*) array
uint32_t ef_search = 100;
uint32_t k = 100;
uint32_t num_queries = 1000;
uint32_t num_gtruth = 1000;
// Query the index and compute the recall.
run_knn_search(index, queries, gtruth, ef_search, k, num_queries, num_gtruth, dataset_dimension);
}
```
### Datasets from ANN-Benchmarks
ANN-Benchmarks provide HDF5 files for a standard benchmark of near-neighbor datasets, queries and ground-truth results. To index any of these datasets you can use the `construct_npy.cpp` and `query_npy.cpp` files linked above.
To generate the [ANNS benchmark datasets](https://github.com/erikbern/ann-benchmarks?tab=readme-ov-file#data-sets), run the following script
```shell
$ ./bin/download_anns_datasets.sh <dataset-name> [--normalize]
```
For datasets that use the angular/cosine similarity, you will need to use `--normalize` option so that the distances are computed correctly.
Available dataset names include:
```shell
_ mnist-784-euclidean
_ sift-128-euclidean
_ glove-25-angular
_ glove-50-angular
_ glove-100-angular
_ glove-200-angular
_ deep-image-96-angular
_ gist-960-euclidean
_ nytimes-256-angular
```
### Experimental API and Future Extensions
You can find the current work under development under the [development-features](https://github.com/BlaiseMuhirwa/flatnav/blob/main/development-features) directory.
While some of these features may be usable, they are not guarranteed to be stable. Stable features will be expected to be part of the PyPI releases.
The most notable on-going extension that's under development is product quantization.
## Citation
If you find this library useful, please consider citing our associated paper:
```
@article{munyampirwa2024down,
title={Down with the Hierarchy: The'H'in HNSW Stands for" Hubs"},
author={Munyampirwa, Blaise and Lakshman, Vihan and Coleman, Benjamin},
journal={arXiv preprint arXiv:2412.01940},
year={2024}
}
```
Raw data
{
"_id": null,
"home_page": "https://flatnav.net",
"name": "flatnav",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": "blaisemunyampirwa@gmail.com",
"keywords": "similarity search, vector databases, machine learning",
"author": "Benjamin Coleman, Blaise Munyampirwa, Vihan Lakshman",
"author_email": "benjamin.ray.coleman@gmail.com, blaisemunyampirwa@gmail.com, vihan@mit.edu",
"download_url": "https://files.pythonhosted.org/packages/ea/17/24f0a463c9c84aa491442fb295b5a28a81b81330975a3cff2d9e532175a9/flatnav-0.1.2.tar.gz",
"platform": null,
"description": "## FlatNav \n\nFlatNav is a fast and header-only graph-based index for Approximate Nearest Neighbor Search (ANNS). FlatNav is inspired by the influential [Hierarchical Navigable Small World (HNSW) index](https://github.com/nmslib/hnswlib), but with the hierarchical component removed. As detailed in our [research paper](https://arxiv.org/pdf/2412.01940), we found that FlatNav achieved identical performance to HNSW on high-dimensional datasets (dimensionality > 32) with approximately 38% less peak memory consumption and a simplified implementation. \n\nWe hope to maintain this open source library as a resource for broader community. Please consider opening a Github Issue for bugs and feature requests, or get in touch with us directly for discussions.\n\n\n### Installation \nFlatNav is implemented in C++ with a complete Python extension with [cereal](https://uscilab.github.io/cereal/) as the only external dependency. This is a header-only library, so there is nothing to build. You can just include the necessary headers in your existing code. \n\nFlatNav is supported on x86-64 machines on linux and MacOS (we can extend this to windows if there is sufficient interest). To get the C++ library working and run examples under the [tools](https://github.com/BlaiseMuhirwa/flatnav/blob/main/tools) directory, you will need\n\n* C++17 compiler with OpenMP support (version >= 2.0)\n* CMake (version >= 3.14)\n\nWe provide some helpful scripts for installing the above in the [bin](https://github.com/BlaiseMuhirwa/flatnav/tree/main/bin) directory. \n\nTo generate the library with CMake and compile examples, run \n\n```shell\n$ git clone https://github.com/BlaiseMuhirwa/flatnav.git --recurse-submodules\n$ cd flatnav\n$ ./bin/build.sh -e\n```\n\nYou can get all options available with the `build.sh` script by passing it the `-h` argument.\n\nThis will display all available build options:\n\n```shell\nUsage ./build.sh [OPTIONS]\n\nAvailable Options:\n -t, --tests: Build tests\n -e, --examples: Build examples\n -v, --verbose: Make verbose\n -b, --benchmark: Build benchmarks\n -bt, --build_type: Build type (Debug, Release, RelWithDebInfo, MinSizeRel)\n -nmv, --no_simd_vectorization:Disable SIMD instructions\n -h, --help: Print this help message\n\nExample Usage:\n ./build.sh -t -e -v\n```\n\nTo build the Python bindings, follow instructions [here](https://github.com/BlaiseMuhirwa/flatnav/blob/main/flatnav_python/README.md). There are also examples for how to use the library to build an index and run queries on top of it [here](https://github.com/BlaiseMuhirwa/flatnav/blob/main/flatnav_python/unit_tests/test_index.py).\n\n### Support for SIMD Extensions \n\nWe currently support SIMD extensions for certain platforms as detailed below. \n\n| Operation | x86_64 | arm64v8 | Apple silicon |\n|-----------|--------|---------|-----------------|\n| FP32 Inner product |SSE, AVX, AVX512 | No SIMD support | No SIMD support |\n| FP32 L2 distance |SSE, AVX, AVX512| No SIMD support | No SIMD support |\n| UINT8 L2 distance |AVX512 | No SIMD support | No SIMD support |\n| INT8 L2 distance | SSE | No SIMD support | No SIMD support |\n\n\n### Getting Started in Python\n\nCurrently, we support Python wheels for versions 3.8 through 3.12 on x86_64 architectures (Intel, AMD and MacOS). Support for \nARM wheels is a future improvement. \n\nThe python library can be installed from PyPI by using \n```shell\n$ pip install flatnav\n```\n\nSimilarly, `flatnav` can be installed from source via [cibuildwheel](https://cibuildwheel.pypa.io/en/stable/), which \nbuilds cross-platform wheels. Follow the following steps\n\n```shell\n$ git clone https://github.com/BlaiseMuhirwa/flatnav.git --recurse-submodules\n$ cd flatnav\n$ make install-cibuildwheel\n\n# This will build flatnav for the current version in your environment. If you want to build wheels \n# for all supported python versions (3.8 to 3.12), remove the --current-version flag.\n$ ./cibuild.sh --current-version\n\n$ pip install wheelhouse/flatnav*.whl --force-reinstall\n```\n\nOnce you have the python library installed and you have a dataset you want to index as a numpy array, you can construct the index as shown below. This will allocate memory and create a directed graph with vectors as nodes.\n\n```python\nimport numpy as np\nimport flatnav\nfrom flatnav.data_type import DataType \n\n# Get your numpy-formatted dataset.\ndataset_size = 1_000_000\ndataset_dimension = 128\ndataset_to_index = np.random.randn(dataset_size, dataset_dimension)\n\n# Define index construction parameters.\ndistance_type = \"l2\"\nmax_edges_per_node = 32\nef_construction = 100\nnum_build_threads = 16\n\n# Create index configuration and pre-allocate memory\nindex = flatnav.index.create(\n distance_type=distance_type,\n index_data_type=DataType.float32,\n dim=dataset_dimension,\n dataset_size=dataset_size,\n max_edges_per_node=max_edges_per_node,\n verbose=True,\n collect_stats=True,\n)\nindex.set_num_threads(num_build_threads)\n\n# Now index the dataset \nindex.add(data=dataset_to_index, ef_construction=ef_construction)\n```\n\nNote that we specified `DataType.float32` to indicate that we want to build an index with vectors represented with `float` type. If you want to use a different precision, such as `uint8_t` or `int8_t` (which are the only other ones currently supported), you can use `DataType.uint8` or `DataType.int8`.\nThe distance type can either be `l2` or `angular`. The `collect_stats` flag will record the number of distance evaluations.\n\nTo query the index we just created by generating IID vectors from the standard normal distribution, we do it as follows \n\n```python\n\n# Set query-time parameters \nk = 100\nef_search = 100\n\n# Run k-NN query with a single thread.\nindex.set_num_threads(1)\n\nqueries = np.random.randn(1000, dataset_to_index.shape[1])\nfor query in queries:\n distances, indices = index.search_single(\n query=query,\n ef_search=ef_search,\n K=k,\n )\n\n```\n\nYou can parallelize the search by setting the number of threads to a desired number and using a different API that also returns the exact same results as `search_single`.\n\n```python\nindex.set_num_threads(16)\ndistances, indices = index.search(queries=queries, ef_search=ef_search, K=k)\n```\n\n### Getting Started in C++\n\nAs mentioned earlier, there is nothing to build since this is header-only. We will translate the above Python code in C++ to illustrate how to use the C++ API. \n\n```c++\n#include <cstdint>\n#include <flatnav/index/Index.h>\n#include <flatnav/distances/SquaredL2Distance.h>\n#include <flatnav/distances/DistanceInterface.h>\n\ntemplate <typename dist_t>\nvoid run_knn_search(Index<dist_t, int>>* index, float *queries, int* gtruth, \n int ef_search, int K, int num_queries, int num_gtruth, int dim) {\n\n float mean_recall = 0;\n for (int i = 0; i < num_queries; i++) {\n float *q = queries + dim * i;\n int *g = gtruth + num_gtruth * i;\n std::vector<std::pair<float, int>> result =\n index->search(q, K, ef_search);\n\n float recall = 0;\n for (int j = 0; j < K; j++) {\n for (int l = 0; l < K; l++) {\n if (result[j].second == g[l]) {\n recall = recall + 1;\n }\n }\n }\n recall = recall / K;\n mean_recall = mean_recall + recall;\n }\n}\n\n\nint main(int argc, char** argv) {\n uint32_t dataset_size = 1000000;\n uint32_t dataset_dimension = 128;\n\n // We skip the random data generation, but you can do that with std::mt19937, std::random_device \n // and std::normal_distribution\n // std::vector<float> dataset_to_index; \n\n uint32_t max_edges_per_node = 32;\n uint32_t ef_construction = 100;\n\n // Create an index with l2 distance \n auto distance = SquaredL2Distance<>::create(dataset_dimension);\n auto* index = new Index<SquaredL2Distance<DataType::float32>>, int>(\n /* dist = */ std::move(distance), /* dataset_size = */ dataset_size,\n /* max_edges_per_node = */ max_edges_per_node);\n\n index->setNumThreads(build_num_threads);\n\n std::vector<int> labels(dataset_size);\n std::iota(labels.begin(), labels.end(), 0);\n index->template addBatch<float>(/* data = */ (void *)dataset_to_index,\n /* labels = */ labels,\n /* ef_construction */ ef_construction);\n\n // Now query the index and compute the recall \n // We assume you have a ground truth (int*) array and a queries (float*) array\n uint32_t ef_search = 100;\n uint32_t k = 100;\n uint32_t num_queries = 1000;\n uint32_t num_gtruth = 1000;\n \n // Query the index and compute the recall. \n run_knn_search(index, queries, gtruth, ef_search, k, num_queries, num_gtruth, dataset_dimension);\n}\n\n```\n\n### Datasets from ANN-Benchmarks\n\nANN-Benchmarks provide HDF5 files for a standard benchmark of near-neighbor datasets, queries and ground-truth results. To index any of these datasets you can use the `construct_npy.cpp` and `query_npy.cpp` files linked above.\n\nTo generate the [ANNS benchmark datasets](https://github.com/erikbern/ann-benchmarks?tab=readme-ov-file#data-sets), run the following script\n\n```shell\n$ ./bin/download_anns_datasets.sh <dataset-name> [--normalize]\n```\n\nFor datasets that use the angular/cosine similarity, you will need to use `--normalize` option so that the distances are computed correctly. \n\nAvailable dataset names include:\n\n```shell\n_ mnist-784-euclidean\n_ sift-128-euclidean\n_ glove-25-angular\n_ glove-50-angular\n_ glove-100-angular\n_ glove-200-angular\n_ deep-image-96-angular\n_ gist-960-euclidean\n_ nytimes-256-angular\n```\n\n### Experimental API and Future Extensions \n\nYou can find the current work under development under the [development-features](https://github.com/BlaiseMuhirwa/flatnav/blob/main/development-features) directory. \nWhile some of these features may be usable, they are not guarranteed to be stable. Stable features will be expected to be part of the PyPI releases. \nThe most notable on-going extension that's under development is product quantization.\n\n## Citation\nIf you find this library useful, please consider citing our associated paper:\n\n```\n@article{munyampirwa2024down,\n title={Down with the Hierarchy: The'H'in HNSW Stands for\" Hubs\"},\n author={Munyampirwa, Blaise and Lakshman, Vihan and Coleman, Benjamin},\n journal={arXiv preprint arXiv:2412.01940},\n year={2024}\n}\n```\n",
"bugtrack_url": null,
"license": "Apache License, Version 2.0",
"summary": "A performant graph-based kNN search library with re-ordering.",
"version": "0.1.2",
"project_urls": {
"Bug Tracker": "https://github.com/BlaiseMuhirwa/flatnav/issues",
"Documentation": "https://blaisemuhirwa.github.io/flatnav",
"Homepage": "https://flatnav.net",
"Source Code": "https://github.com/BlaiseMuhirwa/flatnav"
},
"split_keywords": [
"similarity search",
" vector databases",
" machine learning"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "2d4706802671bf6a86d50b8febf7a0f7b7d0e1f884dc613bd484eb4464ed6082",
"md5": "0f23b1d44f6012493f6b6533c1a7a15c",
"sha256": "501aa33499eaecc23e9d48c5b8aef98baf206627ef80e9a64c06710404652f7d"
},
"downloads": -1,
"filename": "flatnav-0.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "0f23b1d44f6012493f6b6533c1a7a15c",
"packagetype": "bdist_wheel",
"python_version": "cp310",
"requires_python": null,
"size": 459898,
"upload_time": "2025-01-15T08:27:11",
"upload_time_iso_8601": "2025-01-15T08:27:11.104815Z",
"url": "https://files.pythonhosted.org/packages/2d/47/06802671bf6a86d50b8febf7a0f7b7d0e1f884dc613bd484eb4464ed6082/flatnav-0.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "cd8f705941091e2f9d63d492ea16ff8298f904576c0ac2cf1ec406d04b0b25d0",
"md5": "22464d6e724bc2d58d48cf546b4fa956",
"sha256": "c5d7d5e971aa55fb73f995ad2855d9c6cd481bed3bb1e35db26228e4fa1a92e4"
},
"downloads": -1,
"filename": "flatnav-0.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "22464d6e724bc2d58d48cf546b4fa956",
"packagetype": "bdist_wheel",
"python_version": "cp311",
"requires_python": null,
"size": 459616,
"upload_time": "2025-01-15T08:27:12",
"upload_time_iso_8601": "2025-01-15T08:27:12.528620Z",
"url": "https://files.pythonhosted.org/packages/cd/8f/705941091e2f9d63d492ea16ff8298f904576c0ac2cf1ec406d04b0b25d0/flatnav-0.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "ad1f31782e4d45fe48b97393ce24379e007cdd9a2d9ec5be35a46dca7232ceb2",
"md5": "06f1a7e0564a484042e4e0e15f6549d8",
"sha256": "16b21323ce99b63da53328c0662f59cab62e2392f35f11ad2e6f9e3d39a76813"
},
"downloads": -1,
"filename": "flatnav-0.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "06f1a7e0564a484042e4e0e15f6549d8",
"packagetype": "bdist_wheel",
"python_version": "cp312",
"requires_python": null,
"size": 455913,
"upload_time": "2025-01-15T08:27:14",
"upload_time_iso_8601": "2025-01-15T08:27:14.933781Z",
"url": "https://files.pythonhosted.org/packages/ad/1f/31782e4d45fe48b97393ce24379e007cdd9a2d9ec5be35a46dca7232ceb2/flatnav-0.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "01aa84780ff6856d8db0ce5823af83bf6fa13fafa971a44a4ed2f1f3f7044031",
"md5": "9bb681d78ec3fac42202c5d39674d91f",
"sha256": "9cf50abe449a906f67706406166f968459d6cfc45327d5182aca939deb2ad642"
},
"downloads": -1,
"filename": "flatnav-0.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "9bb681d78ec3fac42202c5d39674d91f",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": null,
"size": 459330,
"upload_time": "2025-01-15T08:27:17",
"upload_time_iso_8601": "2025-01-15T08:27:17.783016Z",
"url": "https://files.pythonhosted.org/packages/01/aa/84780ff6856d8db0ce5823af83bf6fa13fafa971a44a4ed2f1f3f7044031/flatnav-0.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "d1fe7a60f17c4e2163db1f01c39d22308b10be62eb13618b4086daa9b13c331c",
"md5": "0c5e30125fb3e70400f37bb15aea8a08",
"sha256": "38e89bfa0d9ee3240fddb16b949d47682b0dde7c44b23f8570b03e9c58d615e7"
},
"downloads": -1,
"filename": "flatnav-0.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "0c5e30125fb3e70400f37bb15aea8a08",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": null,
"size": 458389,
"upload_time": "2025-01-15T08:27:19",
"upload_time_iso_8601": "2025-01-15T08:27:19.233462Z",
"url": "https://files.pythonhosted.org/packages/d1/fe/7a60f17c4e2163db1f01c39d22308b10be62eb13618b4086daa9b13c331c/flatnav-0.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "ea1724f0a463c9c84aa491442fb295b5a28a81b81330975a3cff2d9e532175a9",
"md5": "468cb3e30e7234a186ed1354986d1cdf",
"sha256": "3f306c881a56408425143a2bfbca68eb6e4a3092678a49a962aba9139d8611fa"
},
"downloads": -1,
"filename": "flatnav-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "468cb3e30e7234a186ed1354986d1cdf",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 22258,
"upload_time": "2025-01-15T08:27:21",
"upload_time_iso_8601": "2025-01-15T08:27:21.507376Z",
"url": "https://files.pythonhosted.org/packages/ea/17/24f0a463c9c84aa491442fb295b5a28a81b81330975a3cff2d9e532175a9/flatnav-0.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-15 08:27:21",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "BlaiseMuhirwa",
"github_project": "flatnav",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "flatnav"
}