Skip to main content

anydist

Approximate nearest-neighbor search on arbitrary data with arbitrary dissimilarity functions, using Hierarchical Navigable Small World graphs (HNSW; Malkov & Yashunin).

Unlike most HNSW libraries, which index numeric vectors under a fixed set of metrics, this one accepts any Python objects and any dissimilarity function you write — including non-metric ones. It also has the usual fast path: a set of built-in metrics over numeric vectors, computed in SIMD C kernels with no Python on the hot path, with parallel bulk insertion and parallel batch queries. Between the two there is a third door: hand it a C function pointer — from numba, ctypes, Cython or an extension module of your own — and your dissimilarity runs on that same GIL-free hot path (see C metrics).

The index stays live. You can add to it and delete from it after it is built, interleaved with queries, without a rebuild — which not every library offers: nmslib and annoy are batch indexes, where a point added after the build costs a full rebuild to become findable (see Living indexes).

Two implementations:

  • HNSWFast — the one you want: a Cython implementation over typed, array-backed data structures. HNSW is an alias for it, and a normal install builds it.
  • HNSWPure — the pure-Python, dict-based reference implementation, no dependencies beyond the standard library. It exists to document the algorithm readably and to check the fast path against, not as a deployment option: with a built-in metric it is ~30x slower to build and ~40x slower to query (measured on 5k 20-dimensional vectors), and even with a Python callback — where the callback itself dominates — it is ~1.5x slower.

HNSW never falls back to HNSWPure. Without the compiled extension there is no HNSW, and asking for it raises ImportError saying so — an order of magnitude is not something to lose silently to a build that did not happen. If you want the pure implementation, name it:

from anydist import HNSWPure as HNSW      # deliberately, penalty and all

In a hurry? Quickstart is two code blocks, and the API reference documents every constructor argument and method in one place — go straight there if you would rather read signatures than prose.

This code has been developed together with fishdbc, an incremental density-based clustering algorithm that leverages this library for scalability and allowing arbitrary dissimilarity functions.

Installation

pip install anydist

or, from a checkout of this repository:

pip install .

Windows is not supported: MSVC, the compiler Windows Pythons are built with, lacks the atomic builtins the parallel code needs. Under WSL, the Linux wheels work as usual.

PyPI has prebuilt wheels for the common platforms. Anywhere else, and from a checkout, pip compiles the Cython extension, so you need a C compiler with OpenMP; on macOS, brew install libomp first (or point LIBOMP_PREFIX at another libomp). That extension is what gives you HNSWFast. A failed build shows up the first time you ask for HNSW, since there is no fallback to hide it, but you can check directly:

import anydist
assert anydist.HNSWFast is not None

On x86-64 Linux, the portable build already selects AVX2 kernels at load time on CPUs that have them, so it is not leaving the obvious speedup on the table. For a further boost tied to the machine you build on (at the cost of a non-portable binary):

HNSW_NATIVE=1 pip install .

Quickstart

Arbitrary objects, your own dissimilarity function:

from anydist import HNSW

def jaccard(a, b):
    return 1 - len(a & b) / len(a | b)

index = HNSW(jaccard)                       # the dissimilarity is the 1st argument
index.update(my_sets)                       # insert them all
neighbors = index.search(query_set, k=10)   # [(id, distance), ...], nearest first

Ids are insertion order: the id in a result is the position of that element in the sequence you inserted. The index is a read-only sequence of those elements, so index[id] gives one back, len(index) counts them, and iteration and slicing work.

Numeric vectors and a built-in metric — no Python callback on the hot path:

index = HNSW('euclidean')            # vector length inferred from the first row
index.update(X)                      # X: (n, dim) array, inserted in parallel
I, D = index.search_batch(Q, k=10)   # (n_queries, k) ids and distances, in parallel

With a built-in metric, update inserts across all cores; with a callback metric it stays serial by default, since only you can say whether your function is safe to run on several threads — pass num_threads=N to opt in. add(elem) is still there for genuinely incremental use, where you interleave inserts and queries.

index[id] works in both modes, but they store elements differently: a callback metric keeps your objects, while a built-in metric keeps rows of a typed matrix and hands you back a copy of one — rounded to float32, since that is the default storage there (see float32).

Multithreading

update and search_batch both take num_threads: None (the default) picks for you, a positive value is a thread count, and -1 (or any value ≤ 0) means one thread per CPU.

The rule is that anything we wrote defaults to every core, anything you wrote defaults to one, because only you can say whether your dissimilarity function d — the callable you passed as metric — is thread-safe. What you get once you opt in depends on the metric, because the GIL does:

  • Built-in metric — ours, and stateless, so the kernels are nogil and the threads are real on any interpreter build. Measured on 12 CPUs: 5.5x build / 5.0x query at 50k x 32, and 4.3x / 3.7x at Fashion-MNIST's 60k x 784. Don't read those as an efficiency: the machine's cores are not all the same speed, so what a given box can offer is its own question.
  • C metric (a function pointer you supplied) — the call is nogil too, so opting in is the only way a user-written distance gets real threads on a stock interpreter: measured 4.3x at one-per-core (n=4k, dim=16, a deliberately slow jit_metric). It still defaults to serial, because nogil buys the ability to run your d in parallel, not the safety of doing so — mutable state reached through user_data, a memo table, a counter, all race, with no GIL here to accidentally serialise them.
  • Callback metric — the callback runs under a thread state re-attached per distance. On a free-threaded interpreter that is real parallelism; on a stock GIL build it pays off only when each distance holds the GIL briefly and releases it for a long time — measured 3.0x at 2 threads for a 40k-dimensional NumPy distance, but slower than serial at 128 dimensions, where contention and the thread-state attach cost more than the op. Measure before believing.

These figures are indicative, not a spec — see About these numbers for the method and its resolution limits.

index.update(X)                              # built-in metric: all cores
index.update(objs)                           # your metric: one thread
index.update(objs, num_threads=-1)           # opt in: "my d is thread-safe"
index.update(objs, num_threads=4)            # opt in, four threads
I, D = index.search_batch(Q, num_threads=1)  # force a single-threaded query

search_batch is worth calling for a callback metric even serially: it is the same graph walk as a search loop, but it hands back the whole batch as two (n_queries, k) arrays — neighbor ids and their distances — instead of a list of lists.

chunksize, the constructor's or a call's own, interacts with this: workers take chunks of consecutive ids, which is what keeps parallel insertion correct when the input is order-local (sorted, time-series, grouped records) — two near neighbors inserted at the same instant never see each other, and the edge between them is lost. The default adapts to the batch; raise it if your input is strongly ordered.

Writing a callback metric that scales

On a free-threaded interpreter there is one rule, and it is not about how you write the function — it is about what the function reads:

A parallel metric must not read a shared mutable object on every call. Reading self.something or a closed-over variable costs 3-4x at eight threads. Read parameters from module globals or default arguments instead.

CPython gives some objects immortal or deferred reference counts, so touching them from many threads is free: None, small ints, literals, module-level functions, and all classes (including ones built at runtime by type(), namedtuple, or a class statement inside a function). Everything else is ordinary, and an object that every thread increfs on every distance call turns one cache line into a contention point. Measured on the same laptop (n=10 000 tuples of 8 floats, ef=25, 8 threads, free-threaded 3.13):

metric written as speedup at 8 threads
module-level def, reads only its arguments 3.53x
closure that reads its captured variable 0.82x — slower than serial
closure that ignores it 3.08x
bound method that reads self 1.19x
bound method that ignores self 2.94x

Note the pairs: same callable form, same arithmetic, only the read differs. The form is irrelevant — functools.partial and lambdas are fine. So a metric with parameters should carry them like this:

# slow in parallel: `self.p` and the closure cell are one shared object each
class Metric:
    def __call__(self, a, b):
        return sum((x - y) ** self.p for x, y in zip(a, b))

# all of these scale (3.3-3.5x measured):
P = 2
def metric(a, b):                    # module-level global
    return sum((x - y) ** P for x, y in zip(a, b))

def make_metric(p):
    def metric(a, b, p=p):           # default-argument binding
        return sum((x - y) ** p for x, y in zip(a, b))
    return metric

Per-element objects are not a problem: your data points are thousands of distinct objects, so the traffic spreads. It is the single shared one that hurts.

If threading makes things slower, suspect numpy. There is a bug in numpy (numpy#32298) where an array that has been through pickle, copy.deepcopy, or a joblib cache ends up sharing one small internal object that every thread has to synchronise on. The array is otherwise identical, and single-threaded work is unaffected — but on a free-threaded build the contention can turn a speedup into a slowdown.

Usually you need not care: update() detects and repairs it whenever you hand it an array. The case it cannot reach is a list of rows you extracted yourself, because then the library never sees the array. Repair it at the source:

from anydist import canonicalize_dtype
rows = list(canonicalize_dtype(X))       # X came from pickle / joblib / a cache
index.update(rows, num_threads=-1)

So: parallel inserts slower than serial, on data that came out of a cache, is worth one try of the two lines above — see BUGS.md, which also lists the other issue worth knowing about.

Built-in metrics

Passing a name instead of a callable computes the distance in a SIMD C kernel with no Python on the hot path. Available names (aliases in parentheses):

Name Distance Notes
euclidean (l2) sqrt(sum (x-y)^2)
l2sqr (sqeuclidean) sum (x-y)^2 ranks identically to l2, skips the sqrt
cosine (cosinesimil) 1 - cos_sim normalizes for you; the value scipy and sklearn report
angular (angulardist) arccos(cos_sim) / pi normalizes for you; ranks as cosine does, and obeys the triangle inequality
dot 1 - dot(x, y) normalizes nothing — cosine distance only if you pre-normalized
negdotprod -dot(x, y) the same space as dot, shifted by 1
l1 (manhattan) sum |x-y|
linf (chebyshev) max |x-y|

The names follow nmslib's dense-vector spaces. dot and negdotprod differ by an additive constant, and every decision the search makes is a comparison, so they build identical graphs and return identical orders — choose between them by the number you want back. dot is non-negative on unit-length rows, which a downstream consumer of the weights may need; negdotprod is what code ported from nmslib expects.

Cosine distance

Three names get you cosine-like behavior, and they differ in whether they normalize for you and in what number comes back. All three return the same neighbor lists on unit-length rows, so this is a choice about values and cost, not about quality.

Just want cosine distance? Use cosine. It divides by the length of each of the two vectors itself, so your rows do not have to be unit-length, and it returns 1 - cos_sim — the same value scipy, scikit-learn, nmslib (cosinesimil) and hnswlib report.

index = HNSW('cosine')
index.update(X)                          # any lengths; normalizing is on us
index.search(q, k=10)                    # distances are 1 - cos(q, x), in [0, 2]

Need the triangle inequality? Use angular. Same normalization, but it returns the angle between the vectors scaled to [0, 1] instead of 1 - cos_sim. Since a smaller angle is exactly a larger cosine, it ranks candidates identically to cosine; unlike cosine, it is a true metric on directions. The extra arccos is not free — it is the only libm call in any built-in kernel, and it costs roughly a third of query time at 50 dimensions — so reach for angular when something downstream needs metric behavior, not by default.

Already normalized, and want the cheapest option? Use dot. It normalizes nothing, so on unit rows 1 - dot(x, y) is cosine distance, computed with a single accumulator instead of three.

import numpy as np
from anydist import HNSW

X = np.asarray(X, dtype=np.float64)
X /= np.linalg.norm(X, axis=1, keepdims=True)   # unit rows (drop zero rows first)

index = HNSW('dot')
index.update(X)

q = q / np.linalg.norm(q)                # the query too: dot normalizes nothing
index.search(q, k=10)

A zero row has no direction, so cosine and angular both report distance 0 for it rather than a NaN.

C metrics: numba, ctypes, an extension module

A built-in metric is fast because it never enters Python; a callback is flexible because it is Python. This section is for when you want both — and it asks more of you than the two options above, so skip it unless a Python callback has turned out to be your bottleneck.

Give HNSW a C function pointer instead of a Python callable, and your distance runs with no GIL and no Python on the hot path, exactly where a built-in metric runs. Parallel insertion and parallel queries then become possible on a stock interpreter — but you have to ask for them:

index.update(X, num_threads=-1)              # -1 = one thread per core
I, D = index.search_batch(Q, k=10, num_threads=-1)

A C metric still defaults to one thread, like a Python callback and unlike a built-in metric. nogil buys the ability to run your distance in parallel, not the safety of doing so: state reached through user_data, a memo table or a counter all race, and there is no GIL here to serialise them by accident. Only you can say whether your d is thread-safe, so the opt-in is yours to make.

The signature is fixed, and it is the one scipy's LowLevelCallable users will recognise:

double f(const double *a, const double *b, Py_ssize_t n, void *user_data);

Writing one in Python: jit_metric

Nobody wants to spell that signature in numba by hand, so the decorator generates it from the two-argument form:

from anydist import HNSW, jit_metric

@jit_metric                          # needs numba installed
def manhattan(a, b):                 # a, b: float64 arrays of length dim
    s = 0.0
    for i in range(a.shape[0]):
        s += abs(a[i] - b[i])
    return s

index = HNSW(manhattan)
index.update(X, num_threads=-1)      # opt in: "my d is thread-safe"
I, D = index.search_batch(Q, k=10, num_threads=-1)

It returns a numba.cfunc, which HNSW recognises by its .address. The body must compile in nopython mode — loops over the two arrays are the idiom, arbitrary Python is not; if you need arbitrary Python, pass the plain function and pay the GIL per distance. @jit_metric(cache=True) persists the compiled kernel across processes, which matters for a benchmark harness that runs one cell per process; it requires the function to live in an importable file.

What the pointer buys, measured on the same 12 CPUs (stock-GIL CPython 3.14, n=5000, dim=32, m=16, ef=100, k=10) with the same distance written both ways — the callback being the obvious numpy one-liner:

metric threads build query
Python callback 1 5.34 s 1.63 ms/q
C metric (jit_metric) 1 0.20 s 0.032 ms/q
C metric 12 0.044 s 0.008 ms/q

The 12-thread row is opt-in (num_threads=-1) — a C metric is serial by default, see Multithreading — and it is safe here because this metric is a pure function of its two arguments.

26x on build and 50x on query before threading, 4.6x and 4.3x more from it. The serial ratio is the part that moves with your metric: a callback whose body does real work narrows it, one written in pure Python widens it. The threading factor is the part a callback cannot have on a stock build at all.

Pointing it at a library you already have

The pointer is duck-typed, so nothing here is imported unless you use it. Any of these shapes is accepted:

  • scipy LowLevelCallable — .function is a PyCapsule, .user_data carries parameters. scipy already normalises numba, ctypes and Cython into it, so this one shape covers all three.
  • numba cfunc — has .address.
  • ctypes function pointer — how an existing .so reaches Python.
  • anything else exposing .address or a .function capsule (plus an optional .user_data), including a PyCapsule from your own extension module.
import ctypes

dbl = ctypes.POINTER(ctypes.c_double)
lib = ctypes.CDLL("./libdtw.so")
lib.dtw_metric.restype = ctypes.c_double
lib.dtw_metric.argtypes = [dbl, dbl, ctypes.c_ssize_t, ctypes.c_void_p]

index = HNSW(lib.dtw_metric)         # DTW, in C, across all cores

Parameters go through user_data, which is read from every one of those shapes: keep them in a struct (or a single ctypes cell) that outlives the index, and expose its address as .user_data next to the pointer. jit_metric does not expose user_data — a closure over module-level constants covers the parameterised case with less ceremony, and a hand-built cfunc or a LowLevelCallable is there when you want the pointer itself.

One thing to know before you take this door: getting the signature wrong crashes the process instead of raising an exception. How much protection you get depends on what you hand over:

All three forms declare their signature somewhere, and all three are checked against double (const double *, const double *, Py_ssize_t, void *), raising a CSignatureWarning if they disagree:

  • A capsule carries it in its name — a scipy.LowLevelCallable, or anything else whose .function is a named PyCapsule. Spelling is not the test: const, spacing, parameter names and Py_ssize_t/ssize_t/ npy_intp all pass; the types have to line up.
  • A ctypes function pointer carries it as _restype_/_argtypes_, and a numba cfunc carries the same on its .ctypes view. Either pointer spelling is accepted for the vectors — POINTER(c_double) or a bare c_void_p. jit_metric builds the signature for you, so that path cannot disagree in the first place.

What is genuinely unchecked is a bare address: an unnamed capsule, or a pointer whose declaration you never wrote down. There the crash contract stands in full.

The check warns rather than refuses, because it recognises a list of spellings rather than parsing C, and an unfamiliar typedef must not be able to block a pointer that is in fact correct. If that is your situation:

warnings.simplefilter("ignore", anydist.CSignatureWarning)

and please open an issue with the spelling, so the list can grow.

One limit is worth knowing before you write the metric rather than after: your vectors must all be the same length. n is a single dimension, inferred from the first row and passed to every call. Variable-length data stays on the callback path, which imposes nothing on what your objects are.

Otherwise a C metric follows the built-in-metric path rather than the callback one, which the API reference covers argument by argument. One of those defaults is worth a second look, because it is the only place that path assumes something about your function rather than ours: select is chosen from the vector length, on the reasoning that a short vector is cheap to re-measure. That holds for the built-in kernels, whose work is linear in the length. If yours is expensive at any length — an alignment, a search, a simulation — pass select='edges' and it will read stored weights instead of calling you O(m²) times per row.

API

Constructor

HNSW(metric, m=5, ef=200, m0=None, level_mult=None,
     level_strategy='random', keep_pruned=False, select=None, cache=None,
     float32=None, keep_workspace=False, chunksize=None)
  • metric — the dissimilarity, in one of three forms (it need not satisfy the metric axioms in any of them — the name follows the scikit-learn/hdbscan convention, not a metricity requirement): a callable d(x, y) over arbitrary data; a built-in name (see above) over numeric vectors, whose length is inferred from the first one inserted; or a C function pointer over such vectors (numba cfunc, scipy.LowLevelCallable, ctypes pointer, capsule), which runs GIL-free like a built-in one — see C metrics. A pointer is recognised before callability, so an object that is both takes the fast door.

  • m, ef, m0, level_mult — the paper's parameters; see Malkov & Yashunin.

  • level_strategy — how insertion levels are assigned. 'random' is the paper's strategy and the best choice for approximate nearest-neighbor search; 'balanced' is a deterministic variant (an element is promoted when its neighborhood is full and no neighbor sits above it) that yields a more balanced structure.

  • keep_pruned — the paper's optional keepPrunedConnections (Alg. 4, lines 15–17). Off by default.

  • select — the neighbor-selection rule. 'edges' answers the paper's pruning heuristic (Alg. 4) from edges the graph already has, spending zero extra distance evaluations; 'distances' is the heuristic as written, recomputing the candidate-to-result distances; 'naive' just takes the m nearest (Alg. 3). HNSWFast defaults to 'edges' for a callback metric, and for a built-in one decides at the first vector: 'distances' while dim <= 4*m (recomputing a short vector beats fetching a stored weight), 'edges' above that — a C metric follows that same first-vector rule. HNSWPure always defaults to 'edges', since every distance there is a Python call.

    That first-vector rule takes vector length as a stand-in for what a distance costs, which is true of the built-in kernels. A C metric that is expensive out of proportion to its vector length should be given select='edges' explicitly — otherwise short rows get 'distances', which evaluates your function O(m²) times per row instead of reading stored weights.

  • cache — reuse, within a single insert or query, distances computed at a previous level of the same descent. Defaults on for a callback metric (13% faster builds, measured), off for a built-in one — and off for a C metric, which is cheap for the same reason — where the check costs about what it saves.

  • float32 — store built-in-metric vectors as float32. Defaults on for a built-in metric, as hnswlib and faiss do: it halves both the vector store and the bandwidth of a query's dominant read. It does round the data, so pass False when the exact float64 distances are the point. True is rejected for the other two metric forms rather than quietly overridden: a callback metric keeps Python objects and has no typed store to narrow, and a C metric reads its rows as const double *, so a float32 store would be reinterpreted rather than converted. Both leave the vectors as float64, and index[i] hands back a copy of the stored row either way.

  • keep_workspace — hold the threads' working memory between calls instead of allocating it fresh each time. Only affects update() and search_batch() with more than one thread.

    Each worker needs a set of buffers sized by the number of nodes in the index, so every threaded call pays an allocation proportional to the index — not to the work you asked for. One big update() never notices. A loop of small ones pays it every time, and it grows as the index does. At 100k nodes and four threads:

    fresh each call kept
    update() in chunks of 200 4.01 ms per chunk 2.46 ms
    search_batch() of 64 rows 2.11 ms 0.37 ms

    Off by default because it is a trade, not a free win: the buffers stay allocated — threads × nodes × 32 bytes, or 44 with cache=True, so 370 to 500 MB at twelve threads and a million nodes — until you call release_workspace(). Peak memory is the same either way; what changes is the floor between calls. Turn it on if you build or query in a stream of small batches, leave it off otherwise, and call release_workspace() when that phase is over.

    Results are identical either way, and the setting survives pickling while the buffers themselves do not.

  • chunksize — how many consecutive ids each worker takes in a threaded update() that does not pass its own; see update below for what the chunk guards. None, the default, adapts it to each batch. HNSWPure accepts and keeps it, with no parallel insertion to schedule.

Methods

  • update(X, num_threads=None, ef=None, chunksize=None) — insert many elements. X is a numeric array for a built-in or C metric — anything array-like will do, and it is converted to C-contiguous float64, so integer rows, float32 rows and lists of lists are all accepted and all stored as float64 — or any sequence of objects for a callback metric. Parallel by default for a built-in metric, serial by default for the two you supply (see Multithreading). chunksize sets how many consecutive ids each worker takes: the chunk is what keeps parallel insertion correct on order-local input (sorted, time-series, grouped), where two near neighbors inserted concurrently would never see each other. None takes the constructor's chunksize, and if that is None too the chunk adapts to the batch; set it, on the index or per call, if you know your input is strongly order-local.

  • add(elem, ef=None) — insert one element, returning nothing; its id is the previous len(index).

  • search(q, k=None, ef=None) — the (approximate) k closest elements to q as (id, distance) pairs, sorted by distance. k=None returns all ef candidates found. Soft-deleted nodes are excluded (see below).

  • search_batch(Q, k=None, ef=None, num_threads=None) — answer many queries at once, optionally in parallel. Q is (n_queries, dim) for a built-in or C metric, or a sequence of objects for a callback one; returns (indices, distances) of shape (n_queries, k) — k defaulting to ef — padded with -1/inf where fewer than k neighbors were found. Both classes have it, with the same rows, widths and padding, so I, D = index.search_batch(Q, k) reads the same either way. HNSWFast returns numpy arrays and can use threads; HNSWPure returns lists of lists, having no numpy, and loops over search, accepting num_threads for signature parity and ignoring it as its update does.

  • index[i], len(index), iteration, slicing — the index is a read-only sequence of the elements you inserted, in id order (negative ids count from the end; a built-in or C metric returns copies of the stored rows). Note this is not what index[i] meant in this package's predecessor, where it yielded node i's neighbors; graphs is where those live now.

  • mark_deleted(i) / unmark_deleted(i) — soft deletion (see below); index.deleted_count reports how many are currently marked.

  • add_recording(elem, ef=None, harvest_cap=0) — insert one element and return every distance computed during the insertion as three parallel sequences (i, j, dist), where i is always the new id — numpy arrays from HNSWFast, and from HNSWPure the standard library's array.array (typecodes 'i', 'i', 'd'), as compact at 16 bytes a triple. A plain add records nothing and pays no overhead.

  • update_recording(X, ..., harvest_cap=0) — the batch form, returning the same three sequences for the whole insertion. It is add_recording in a loop, and given the same insertion levels the two harvest the same triples. Note that HNSWFast's comes back in per-worker order rather than insertion order, so sort by i if you need it.

    harvest_cap bounds what comes back, identically on both: 0 is off, and a positive N keeps only each inserted node's N shortest triples. It does not change the index — the same distances are computed and the same graph is built either way, so it trades recorded detail for the downstream cost of consuming it. Selection is by distance alone, which can keep the wrong bridge when a nearer cluster fills the quota first; leave it off for streaming workloads with concept drift, where insertion order cannot shuffle that away. Any cap >= 1 keeps every node attached, since a node is only ever matched against already-inserted ones.

  • freeze() — give up inserting in exchange for the memory only insertion needed (the stored edge weights: n*m0*8 bytes at the base layer, plus the smaller per-node blocks above it). There is no way back, as with a frozenset. Under select='distances', which stores no weights to begin with, it only installs the guard. Returns None: it freezes the index you call it on rather than handing back a frozen copy.

  • release_workspace() — free the threads' working memory that keep_workspace=True holds on to. Safe to call at any time, including when nothing is held; the next threaded call simply allocates again. Present on both classes, so code holding either can call it without asking which.

  • graphs — the adjacency as a list of {node: {neighbor: dist}}, one per level, for inspection.

Living indexes

An index you can keep changing is not universal among ANN libraries. Adding 2,000 vectors one at a time to a 20k-node index, with a k=10 query every 100 inserts (dim=32, m=16, serial):

library insert after build μs/insert μs/delete
this library yes 61 mark_deleted, 0.1
hnswlib yes 53 yes, 0.1
usearch yes 73 yes, 0.6
faiss (IndexHNSWFlat) yes 129 none

nmslib and annoy are not in the table because they are batch indexes and say so — nmslib: "only static data sets are supported"; annoy: "after calling build, no more items can be added". A point added later is kept, but the structure that answers queries was built once and does not know about it, so getting it in means rebuilding the whole index. That is a different operation from an incremental insert, not a slower one, and there is nothing here to compare it against.

Deletion

mark_deleted(i) drops node i from results; unmark_deleted(i) puts it back, and deleted_count says how many are out. Deletion is soft, as in hnswlib: the node stops appearing in results but stays a routing waypoint, so the graph never gets disconnected and recall for everything else is untouched. The cost is that deleted nodes keep their memory and still cost distance computations while routing, so an index that is mostly deleted wants a rebuild.

index.mark_deleted(7)
ids, dists = index.search_batch(Q, k=10)   # 7 will not appear

The exclusion happens inside the search, not over its output: deleted nodes are still traversed, so what comes back is the true nearest live neighbors — not the unfiltered answer with rows struck out (measured >0.99 recall against brute force over the live set, with half the index deleted). You get min(k, ef, live) results: the search fills its pool with ef live candidates before it is willing to stop, so a short list means there really are that few live nodes, not that the search gave up. (k > ef truncates to ef, deletions or not — that is the pool size, so keep ef comfortably above k as usual.)

Persistence

anydist.save and anydist.load take a path or a binary file object:

import anydist

anydist.save(index, "index.pkl")
index = anydist.load("index.pkl")          # same graph, same results

There is no saves()/loads() pair returning bytes — pass an io.BytesIO(), as with numpy.save. Plain pickle (or dill) still works on an index too; save/load exist for the one thing pickle cannot do on its own.

The metric is the only part that may not travel. A built-in name or a module-level function pickles; a lambda, a closure or a C function pointer does not, and an address would be meaningless in the loading process anyway. Everything expensive — the vectors, the adjacency, the deletions — always serialises, so rebuilding would be a poor answer. Instead, save without the metric and give it back on load:

anydist.save(index, "index.pkl", include_metric=False)
index = anydist.load("index.pkl", metric=my_cfunc)

Including the metric is the default and raises if it cannot be done, so save never quietly writes a file that load(f) cannot open. You get a self-contained file or an exception naming the way out — never a surprise in the process that loads it.

Speed. Saving and loading are close to memory-bandwidth bound, so an index round-trips far faster than it builds. Measured on the machine described under About these numbers, writing to a real file in a temporary directory — as every library in the comparison does, so the figures are like-for-like:

index on disk save load
Fashion-MNIST, 60k x 784 213 MB 0.18 s 0.17 s
Gaussian, 100k x 128 92 MB 0.09 s 0.06 s
Gaussian, 50k x 32 14 MB 0.02 s 0.01 s

Roughly 1.2 GB/s each way — the same order as a memory copy, and two orders of magnitude cheaper than rebuilding the index, which is the comparison that matters. (Nothing is fsynced, so this is the cost of handing the bytes to the OS rather than of a durable write.)

hnswlib is about twice as quick at both, and its file is ~8% smaller (197 MB against 213 MB on Fashion-MNIST): it writes a fixed-size record per element straight out of its own arena, where this library pickles, and it stores no edge weights. Ours keeps them whenever select does — under select='distances', which stores none, the sizes match. Both are far below the cost of building, so the difference is unlikely to be what you optimise.

Only load files you trust. save/load are built on pickle, and unpickling any file runs code that the file chooses. That is pickle's design, not something this library adds or can screen for: load sees the constructor arguments only after the interpreter has already executed whatever the stream asked for. Treat an index file exactly as you would a .py you were about to run — the format is not a safe interchange format for data from elsewhere.

load is strict in both directions: it refuses a metric= for a file that already has one (that would replace a metric known to be right with one it cannot check), and refuses a metric of the wrong kind — the three forms store their elements differently, so a callback cannot be attached to a graph built for a built-in name. What it cannot check is that you passed the same distance; supply a different one and the graph answers for a metric it was not built for. That much is yours to get right.

Under the hood this is a plain pickle using persistent_id, the stdlib's own mechanism for an object the loader must re-supply — there is no bespoke format here to version. The pickle carries the vectors (or the elements), the adjacency, the construction parameters and any soft deletions, so it is self-contained but not small: expect roughly the in-memory size of the index. There is no separate index-only/mmap format.

The two classes

They take the same constructor arguments and answer the same calls, and both accept all three metric forms. What only HNSWFast has is parallelism (update across cores, search_batch across queries), numpy arrays out of the batch API, and the float32 vector store.

HNSWPure accepts num_threads and inserts serially, and rejects float32=True having no typed store to narrow. It accepts keep_workspace=True and ignores it, rather than rejecting it: float32 changes the numbers, so ignoring a request for it would mislead about results, while keep_workspace only decides when memory is allocated and a class with no threads runs the same program either way. It does call C metrics, through ctypes, marshalling both operands on every distance. It is there so the reference implementation can run the metric you are actually indexing with — but that marshalling is a fixed per-call cost, so whether it beats writing the distance in Python depends on how much work the distance itself does. Against the plain Python loop a jit_metric wraps, the pointer measured ~2.8x slower at dim=8 and ~5x faster at dim=64. If you just want a working index at low dimension, that original two-argument function is still reachable on .py_func_2arg.

The two produce equivalent results only distributionally (ties are broken differently), so compare them via k-NN recall rather than exact neighbor lists.

Performance

On the dense-vector case this is in the same class as the established C++ libraries — which is the point, since that case is the one they optimize.

What the generality is and is not. It is not that nobody else indexes non-vector data: nmslib ships jaccard_sparse, leven and others, and is a real competitor on anything in its catalogue. The difference is that a catalogue is fixed at compile time and a Python callable is not. If your dissimilarity is in nmslib's list, use nmslib and expect it to win; if it is a business rule, a weighted ensemble, or anything you would otherwise have to write C++ for, this library will run it — and, with jit_metric, run it at a speed the section below quantifies.

Against the other libraries

50k Gaussian vectors in 32 dimensions, m=16, efConstruction=200, k=10, 12 threads. Each figure is a ratio to this library, formed within a repetition and shown with its range over three of them. A ratio whose range spans 1.0 is not a difference; the resolution limit here is 1.06x on build and 1.07x on query, measured by running the identical build twice as a control arm.

Both columns are times, relative to this library, so in both of them lower is faster and 1.00x is us:

library build time query time recall@ef160
this library 1.00x 1.00x .976
hnswlib 0.8.0 1.03x [0.93-1.10] 0.95x [0.94-1.06] .980
faiss IndexHNSWFlat 1.06x [1.00-1.07] 0.89x [0.88-0.93] .979
nmslib 2.1.2 (AVX2) 1.44x [1.42-1.47] 1.09x [1.06-1.12] .980
usearch 1.36x [1.26-1.41] 1.54x [1.49-1.54] .978
annoy (trees, not HNSW) 0.17x [0.16-0.18] 100x 1.000

hnswlib is indistinguishable from this library on both, which is the result that matters: the reference implementation of the algorithm, and we are inside the noise of it. faiss queries ~11% faster. We build ~1.4x faster than both nmslib and usearch, and query ~1.5x faster than usearch; nmslib's query time is within a whisker of ours. annoy is a tree index included for scale, and is the only arm whose parameters were not swept the way the others' ef was.

Recall is identical to three decimals across every HNSW arm at matched ef, which is the check that the comparison is like-for-like: the same graph quality, differing only in how fast it is reached.

Two caveats. nmslib's PyPI wheel is built without SIMD, and warns so on import; the row above is from a source build (pip install --no-binary :all: --no-build-isolation nmslib — plain --no-binary fails on Python 3.14). And the larger datasets we measured, at 100k x 128 and Fashion-MNIST, had control spreads of 2.0x to 5.6x on this machine, so they are omitted rather than reported: nothing there was resolvable.

An arbitrary metric, measured

kosarak from ann-benchmarks: 74,962 sets of ints, mean size 55.6, Jaccard, recall@10 against the published ground truth, M=16 efC=100. The comparison is nmslib's jaccard_sparse, built from source so it has AVX2 — its purpose-built C++ space against a Python function of ours.

Three ways to give this library the same distance. Every ratio below is formed inside one run, from the minimum of seven alternating repetitions, with a control arm — the same index timed a second time — so the resolution is measured rather than hoped for. The two columns of ratios span ef=100 and ef=400, that is recall 0.90 and 0.95:

how against @jit_metric against nmslib resolution
plain Python callable 7.5–8.1x slower — 1.02x
@jit_metric, 1 thread (pinned) — 1.18–1.20x slower 1.002x
@jit_metric, 12 threads — 1.24–1.26x slower 1.02–1.04x

So compiling the callable is worth about 8x, and once compiled the gap to a purpose-built C++ space is about 1.2x, whether serial or threaded. That is what writing your distance in Python instead of C++ costs.

Each arm queries through whichever API is faster for it: search_batch for the compiled metric, a search loop for the Python callable, which serially on this GIL build beat batching by ~6%. Matching them would widen the 8x slightly, not narrow it.

For scale rather than for comparison, the serial pinned run measured 0.61 ms/query for @jit_metric against nmslib's 0.50 at recall 0.90, and 0.095 against 0.075 at twelve threads. Absolute figures move between runs on this machine — the same compiled index measured 0.61 in one run and 0.73 in another, after a long build had heated the box — which is exactly why the ratios above are formed within a run and the absolutes are not compared across them.

The serial rows are pinned to one core with taskset: this machine's cores are not all the same speed, so an unpinned "1 thread" figure silently depends on which one it landed on — measured 2.6x apart for the same code. See About these numbers.

Variable-length objects reach a C metric through a fixed double* signature by storing [offset, length] in the row and closing over one flat array, so the vector store is 1.2 MB rather than the ~1.5 GB a padded matrix would need, and the distance stays exact:

FLAT = np.concatenate(sorted_sets)      # every set, back to back

@jit_metric
def jaccard(a, b):                      # each row is [offset, length]
    ao, an = int(a[0]), int(a[1])
    bo, bn = int(b[0]), int(b[1])
    i = j = inter = 0
    while i < an and j < bn:            # sorted-merge intersection
        x, y = FLAT[ao + i], FLAT[bo + j]
        if x == y:   inter += 1; i += 1; j += 1
        elif x < y:  i += 1
        else:        j += 1
    return 1.0 - inter / (an + bn - inter)

When an index is worth building at all. On an expensive metric the answer depends on n, because brute force is linear and the graph is not. Jaccard over random sets, ef=400, serial:

n brute force this library speedup
5,000 5.04 ms/q 4.68 ms/q 1.1x
20,000 22.25 ms/q 9.50 ms/q 2.3x
80,000 87.72 ms/q 12.81 ms/q 6.8x

Below ~10k elements an index barely pays for itself; by ~100k it is most of the cost. (Recall drifts 0.955 -> 0.825 across those rows at fixed ef, so part of the last speedup is bought with quality.)

About these numbers

Every figure in this README comes from one throttling laptop, so the method matters more than usual. What was done, and what it means for how much weight a number can carry:

  • A control arm. Every comparison includes the same build measured twice. The spread between those two runs is the resolution limit, and no effect smaller than it is claimed. This is not a formality: it caught a 10x "difference" that was two benchmark processes accidentally running at once, and a 3.8x one that was a metric interpolating across the steep part of a recall curve.
  • Interleaved, paired repetitions. Arms alternate within a repetition and ratios are formed inside it, so drift in the machine is common-mode and cancels. Summarising each arm separately and dividing would not.
  • One process per measurement, with the thread count pinned before any import and the observed value recorded, plus load average, CPU-seconds and timestamps on every row.
  • Pinning, where it decides anything. The cores here are not all the same speed, so an unpinned single-threaded figure depends on which one it landed on — measured 2.6x apart for identical code. Serial comparisons use taskset; thread-count speedups are reported as measured, without being turned into an efficiency against a core count that would not mean much.

What this buys is roughly 3-13% resolution single-threaded and 6-7% on the 50k x 32 dataset at twelve threads. On the larger datasets at twelve threads it buys nothing: identical builds differed by 2.0x to 5.6x, so those cells are reported as unresolvable rather than given numbers. Redoing all of this on a quiet, homogeneous machine would tighten every bound here, and is on the list.

Reproducing any of this. Everything above is produced by benchmarks/ against the anydist you have installed — python run.py then python analyze.py, with every comparison library optional. The harness prints your machine's resolution before it prints any comparison, so you can see which of its own numbers to believe.

Author

Matteo Dell'Amico — della@linux.it

License

BSD 3-clause; see the LICENSE file.

Metadata

Release files for anydist 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for anydist 1.0.1
File Size Uploaded
anydist-1.0.1.tar.gz 204.7 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for anydist 1.0.1
File
anydist-1.0.1-cp315-cp315t-musllinux_1_2_x86_64.whl CPython 3.15 CPython 3.15 free-threading Linux musl 1.2+ x86-64 Details
anydist-1.0.1-cp315-cp315t-musllinux_1_2_aarch64.whl CPython 3.15 CPython 3.15 free-threading Linux musl 1.2+ ARM64 Details
anydist-1.0.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.15 CPython 3.15 free-threading Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
anydist-1.0.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.15 CPython 3.15 free-threading Linux glibc 2.28+ ARM64, Linux glibc 2.17+ ARM64 Details
anydist-1.0.1-cp315-cp315t-macosx_15_0_arm64.whl CPython 3.15 CPython 3.15 free-threading macOS 15.0+ ARM64 Details
anydist-1.0.1-cp315-cp315-musllinux_1_2_x86_64.whl CPython 3.15 CPython 3.15 Linux musl 1.2+ x86-64 Details
anydist-1.0.1-cp315-cp315-musllinux_1_2_aarch64.whl CPython 3.15 CPython 3.15 Linux musl 1.2+ ARM64 Details
anydist-1.0.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.15 CPython 3.15 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
anydist-1.0.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.15 CPython 3.15 Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 Details
anydist-1.0.1-cp315-cp315-macosx_15_0_arm64.whl CPython 3.15 CPython 3.15 macOS 15.0+ ARM64 Details
anydist-1.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ x86-64 Details
anydist-1.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ ARM64 Details
anydist-1.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
anydist-1.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 Details
anydist-1.0.1-cp314-cp314t-macosx_15_0_arm64.whl CPython 3.14 CPython 3.14 free-threading macOS 15.0+ ARM64 Details
anydist-1.0.1-cp314-cp314-musllinux_1_2_x86_64.whl CPython 3.14 CPython 3.14 Linux musl 1.2+ x86-64 Details
anydist-1.0.1-cp314-cp314-musllinux_1_2_aarch64.whl CPython 3.14 CPython 3.14 Linux musl 1.2+ ARM64 Details
anydist-1.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
anydist-1.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 Details
anydist-1.0.1-cp314-cp314-macosx_15_0_arm64.whl CPython 3.14 CPython 3.14 macOS 15.0+ ARM64 Details
anydist-1.0.1-cp313-cp313-musllinux_1_2_x86_64.whl CPython 3.13 CPython 3.13 Linux musl 1.2+ x86-64 Details
anydist-1.0.1-cp313-cp313-musllinux_1_2_aarch64.whl CPython 3.13 CPython 3.13 Linux musl 1.2+ ARM64 Details
anydist-1.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
anydist-1.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 Details
anydist-1.0.1-cp313-cp313-macosx_15_0_arm64.whl CPython 3.13 CPython 3.13 macOS 15.0+ ARM64 Details
anydist-1.0.1-cp312-cp312-musllinux_1_2_x86_64.whl CPython 3.12 CPython 3.12 Linux musl 1.2+ x86-64 Details
anydist-1.0.1-cp312-cp312-musllinux_1_2_aarch64.whl CPython 3.12 CPython 3.12 Linux musl 1.2+ ARM64 Details
anydist-1.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
anydist-1.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 Details
anydist-1.0.1-cp312-cp312-macosx_15_0_arm64.whl CPython 3.12 CPython 3.12 macOS 15.0+ ARM64 Details
anydist-1.0.1-cp311-cp311-musllinux_1_2_x86_64.whl CPython 3.11 CPython 3.11 Linux musl 1.2+ x86-64 Details
anydist-1.0.1-cp311-cp311-musllinux_1_2_aarch64.whl CPython 3.11 CPython 3.11 Linux musl 1.2+ ARM64 Details
anydist-1.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
anydist-1.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 Details
anydist-1.0.1-cp311-cp311-macosx_15_0_arm64.whl CPython 3.11 CPython 3.11 macOS 15.0+ ARM64 Details
anydist-1.0.1-cp310-cp310-musllinux_1_2_x86_64.whl CPython 3.10 CPython 3.10 Linux musl 1.2+ x86-64 Details
anydist-1.0.1-cp310-cp310-musllinux_1_2_aarch64.whl CPython 3.10 CPython 3.10 Linux musl 1.2+ ARM64 Details
anydist-1.0.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
anydist-1.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.10 CPython 3.10 Linux glibc 2.28+ ARM64, Linux glibc 2.17+ ARM64 Details
anydist-1.0.1-cp310-cp310-macosx_15_0_arm64.whl CPython 3.10 CPython 3.10 macOS 15.0+ ARM64 Details

Total release size: 77.9 MB

Release files / anydist-1.0.1.tar.gz

Download URL anydist-1.0.1.tar.gz
Size 204.7 kB
Tags Source
SHA-256 checksum
How to use checksums
46c86b00b9fc39e03d7ebe94a29417c97cad1f49ad407fed5d8c25b103c6ea22
BLAKE2b-256 checksum
How to use checksums
6b8a83d184d94d8069840745207847dd9255cdba9e156dc00540528a7f778b99
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315t-musllinux_1_2_x86_64.whl

Download URL anydist-1.0.1-cp315-cp315t-musllinux_1_2_x86_64.whl
Size 2.3 MB
Tags CPython 3.15 CPython 3.15 free-threading Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
4aecd422c23caa81639537591c6ebd898d624d504afdb695d47cb2cbf41c0ea9
BLAKE2b-256 checksum
How to use checksums
1da93e30875bfd36e020962ebf1a212d009d82a732e6210b7a0669d71112bc76
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315t-musllinux_1_2_aarch64.whl

Download URL anydist-1.0.1-cp315-cp315t-musllinux_1_2_aarch64.whl
Size 2.2 MB
Tags CPython 3.15 CPython 3.15 free-threading Linux musl 1.2+ ARM64
SHA-256 checksum
How to use checksums
39ffe3523596e6407e953da33d8229205ff88769fa931e12384a85824da668b5
BLAKE2b-256 checksum
How to use checksums
e5bf5108dd171a24fa104d00b8efc3ba38d7908ad064c894ded867bc3039a64c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL anydist-1.0.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags CPython 3.15 CPython 3.15 free-threading Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
105772eac586f060df21aaa1a1dfdf436bf3a3f86a5cb271710a298968806f96
BLAKE2b-256 checksum
How to use checksums
be98f92869c28b4862574193edfefc7816781ceb11c380e43a3e7c6f81d14919
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL anydist-1.0.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.3 MB
Tags CPython 3.15 CPython 3.15 free-threading Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
e786669f5db3ec76c5decc0b0d4cd5d5fe5160054c9dbbfdc1fc9cff7d1ddc5a
BLAKE2b-256 checksum
How to use checksums
887a777a1b0dea8b20f33762230240ec17d1e482a3cc95192684f9113abb40a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315t-macosx_15_0_arm64.whl

Download URL anydist-1.0.1-cp315-cp315t-macosx_15_0_arm64.whl
Size 711.7 kB
Tags CPython 3.15 CPython 3.15 free-threading macOS 15.0+ ARM64
SHA-256 checksum
How to use checksums
e2b52cab1fb5b7a339b2ebed5dbeb87115b72cc955f484ea5ec0c5435b1e6326
BLAKE2b-256 checksum
How to use checksums
2a1364649f9365a662e749590fe668a16d07b3d40e8a6197f5b1209a73ca33af
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315-musllinux_1_2_x86_64.whl

Download URL anydist-1.0.1-cp315-cp315-musllinux_1_2_x86_64.whl
Size 2.3 MB
Tags CPython 3.15 Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
142dc4f36d0121584dea04535404002511efa8f6fe8d9c3739bf81296095736c
BLAKE2b-256 checksum
How to use checksums
777fb0ffd6acb71c7571f3a105956918ac37339bb787f1a8be46565d46afbb36
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315-musllinux_1_2_aarch64.whl

Download URL anydist-1.0.1-cp315-cp315-musllinux_1_2_aarch64.whl
Size 2.2 MB
Tags CPython 3.15 Linux musl 1.2+ ARM64
SHA-256 checksum
How to use checksums
c94f86c353e4618460efb5b91429118a1cf5272633795b5e6870127a48cef977
BLAKE2b-256 checksum
How to use checksums
e77eaf643417d94ed7689c11a8897092e9a0e2a12075ca15a5a205d6e232e49d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL anydist-1.0.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags CPython 3.15 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
1ee3554cfa2765ac18c1acd2855b17d2781ad67e8895bbcd5b2fb12d5cde2b92
BLAKE2b-256 checksum
How to use checksums
9701f7e01b1ec19c3eb4762c70958947ad56f4b38b1645fad98d1a7b405deb86
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL anydist-1.0.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.2 MB
Tags CPython 3.15 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
be9fad5a97c8d0446029c29778f5c105364423e2c230b2db5e2032f3fc9bfa8e
BLAKE2b-256 checksum
How to use checksums
0a00e934947807f2d72ac028a20b6a12fa901a8226974ccdcb84c24108fc6a79
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp315-cp315-macosx_15_0_arm64.whl

Download URL anydist-1.0.1-cp315-cp315-macosx_15_0_arm64.whl
Size 693.2 kB
Tags CPython 3.15 macOS 15.0+ ARM64
SHA-256 checksum
How to use checksums
0040beabd47dd0131f75a562c4ee65115ed1ce2ce07c8342f3b3707f79dfa504
BLAKE2b-256 checksum
How to use checksums
8c7fa706f133242a21645698cd4366d381e73c3899a75b2d588031efc9018160
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl

Download URL anydist-1.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl
Size 2.3 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
fe0aa25999944208343bb24f0f62444ae3af60019b615caf10fa12bea6a8d037
BLAKE2b-256 checksum
How to use checksums
5237dedeb6828ff9af9c1fdd2ffd83ee9c1b91444dd6647141d59c28dbf113c9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl

Download URL anydist-1.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl
Size 2.2 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ ARM64
SHA-256 checksum
How to use checksums
5c235eb36d3ad949897b611f0b02ba2719068ec9e54eeee9906917f295333277
BLAKE2b-256 checksum
How to use checksums
d5bd4f68d2dda33822e5e03db4f24c01956681b73122fa018e1adae0d87018ea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL anydist-1.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
07c92285e3acf9cae50313078431f34d0d98f28d556b3251ecc2809d97e87215
BLAKE2b-256 checksum
How to use checksums
0edd6d46a9ce0ac415f598a3e64ea965a356917f344b60ceb1d8a062d2ac76bd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL anydist-1.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.3 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
378517ff8ec4d0fd42c543ef5e8d1850bbda2a9ea543fb699bfa1064b7321286
BLAKE2b-256 checksum
How to use checksums
f7eb60c1f7b85ba9446821148eb814f775d63071c87e2d93097feee7d1012020
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314t-macosx_15_0_arm64.whl

Download URL anydist-1.0.1-cp314-cp314t-macosx_15_0_arm64.whl
Size 713.1 kB
Tags CPython 3.14 CPython 3.14 free-threading macOS 15.0+ ARM64
SHA-256 checksum
How to use checksums
5817bd0d04d1494e25d0d287ec867f53c73ccaf7a1db0f658fde094c26228520
BLAKE2b-256 checksum
How to use checksums
a0ae9ef70d50e285be942c59d31a5d0918b9690b4505a5d14ad7db01990ae57d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314-musllinux_1_2_x86_64.whl

Download URL anydist-1.0.1-cp314-cp314-musllinux_1_2_x86_64.whl
Size 2.3 MB
Tags CPython 3.14 Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
a25b62eb72e77a705ac3cf28e906c7aaad046be5b170f24031c87881e41d312c
BLAKE2b-256 checksum
How to use checksums
e5f99788df3a960d5b221789dbed4ddaf7f032daeee52d4a6e850770240375d7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314-musllinux_1_2_aarch64.whl

Download URL anydist-1.0.1-cp314-cp314-musllinux_1_2_aarch64.whl
Size 2.2 MB
Tags CPython 3.14 Linux musl 1.2+ ARM64
SHA-256 checksum
How to use checksums
adea7882b530171d90d62f928789e2c2539bfe84658e7c6a8b2d51dc7ae70eb9
BLAKE2b-256 checksum
How to use checksums
f7331c616dd3e718743a6e3a9e800b429aaebf4d87935fd26140280463944a11
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL anydist-1.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.2 MB
Tags CPython 3.14 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
9fa0ce409531886aeef16c83b984d41351b7d635e131b2dbe15c411df36aa987
BLAKE2b-256 checksum
How to use checksums
a563f85b26be4cf08c7924357d0c280939433c6906e7c989e8a5d14ecbc8d18e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL anydist-1.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.2 MB
Tags CPython 3.14 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
f8a1c2553c377fda4c02794f3591013f9d7a4ffb67bf3367aeeb7abb8b88eb76
BLAKE2b-256 checksum
How to use checksums
be106eecf1f2169f17bd36e7cfdba566556b7bda987644b384ce4676dbcedbba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp314-cp314-macosx_15_0_arm64.whl

Download URL anydist-1.0.1-cp314-cp314-macosx_15_0_arm64.whl
Size 693.2 kB
Tags CPython 3.14 macOS 15.0+ ARM64
SHA-256 checksum
How to use checksums
04dc72c8cf879039339babd10663f6070d4fc1516cb3f31c27deab07ef944274
BLAKE2b-256 checksum
How to use checksums
c3dc6fb58eeeaecc86781730900866e71a40a678548ffe9e1cc545ed70ba7bc0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp313-cp313-musllinux_1_2_x86_64.whl

Download URL anydist-1.0.1-cp313-cp313-musllinux_1_2_x86_64.whl
Size 2.3 MB
Tags CPython 3.13 Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
2092b1545c4b2856079b5b35a9c1b93fad99f734592c92896997e54475b61e1b
BLAKE2b-256 checksum
How to use checksums
d9d852c5785871a43d137bd062f80c51095d51e3c9b92151c040bdb70af3bc2d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp313-cp313-musllinux_1_2_aarch64.whl

Download URL anydist-1.0.1-cp313-cp313-musllinux_1_2_aarch64.whl
Size 2.2 MB
Tags CPython 3.13 Linux musl 1.2+ ARM64
SHA-256 checksum
How to use checksums
c6a7eb0eb3620189cc9a39b4fd49537d13e0b38d4ceb7d069d9adf65804c4224
BLAKE2b-256 checksum
How to use checksums
273cf40143db1ad94726fcc1f846341f65536bd697c120f48b83c214527f0575
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL anydist-1.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags CPython 3.13 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
0158c7374333aef708864c50b47801a12c1eee55a687c6c56996fe4b848b450c
BLAKE2b-256 checksum
How to use checksums
94f57a0d74c39da9c0161e5e811aee3dcdbce02061f4fbf493c2fd2294b326a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL anydist-1.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.2 MB
Tags CPython 3.13 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
98e2c14295980e5e61801ff1ebc82eab6d2c8714bb8d93c69fbbd98f6a45b6bf
BLAKE2b-256 checksum
How to use checksums
d611e2dea1300c5a806369700f95656abe08efc53977163668247fa1763d8bf4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp313-cp313-macosx_15_0_arm64.whl

Download URL anydist-1.0.1-cp313-cp313-macosx_15_0_arm64.whl
Size 691.5 kB
Tags CPython 3.13 macOS 15.0+ ARM64
SHA-256 checksum
How to use checksums
760fb65c2074a45e67f0f4aa02f48dc5811c609c2843ab08a5c82839598f5396
BLAKE2b-256 checksum
How to use checksums
47d1a87cb581412916531b0069042a17646ef7c6c00f61739542c94e172feed9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp312-cp312-musllinux_1_2_x86_64.whl

Download URL anydist-1.0.1-cp312-cp312-musllinux_1_2_x86_64.whl
Size 2.3 MB
Tags CPython 3.12 Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
cfa8dd1923f7f2431a637a941d684010e247a3501a3be2c3714af4da02192de1
BLAKE2b-256 checksum
How to use checksums
dc0a08f9ef1a24db31bda76b35d5d862e612ab2d6e76ad78eeb2ed5c7e08b360
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp312-cp312-musllinux_1_2_aarch64.whl

Download URL anydist-1.0.1-cp312-cp312-musllinux_1_2_aarch64.whl
Size 2.2 MB
Tags CPython 3.12 Linux musl 1.2+ ARM64
SHA-256 checksum
How to use checksums
7a48d3cda6144ed6e6c433463a89353c3b1d320a19637ce02cfadafbd30f957c
BLAKE2b-256 checksum
How to use checksums
caa671e61ca79ced45eec489c56efea06b597fdce57b03addaf0fa91c6f34b6c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL anydist-1.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
65d059c496acc7badaa02dab2a56c59135f92490ae2598e3c5aa2b84142be6c9
BLAKE2b-256 checksum
How to use checksums
92ec09d45b2ccb9c81c4761ad69291611f55ebb43698d44d0565987b5999edcd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL anydist-1.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.2 MB
Tags CPython 3.12 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
25655381cfad08fb75f32ab6a7d4af7a7763352cf80307aae2431bb0dddbe50d
BLAKE2b-256 checksum
How to use checksums
e9575a268e14b3d7d51667936ccf9a47e4c0b78345b036b84a25282f8fcdb61f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp312-cp312-macosx_15_0_arm64.whl

Download URL anydist-1.0.1-cp312-cp312-macosx_15_0_arm64.whl
Size 688.7 kB
Tags CPython 3.12 macOS 15.0+ ARM64
SHA-256 checksum
How to use checksums
a06e10235c9f994e38c917dd3953bf472f2a6a05bc87f03a7599c0a181ea3ad3
BLAKE2b-256 checksum
How to use checksums
3860e70a777655971209fc8214fe03876091f8245995a74ab174647a7a2a5f57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp311-cp311-musllinux_1_2_x86_64.whl

Download URL anydist-1.0.1-cp311-cp311-musllinux_1_2_x86_64.whl
Size 2.4 MB
Tags CPython 3.11 Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
6592122aba751c53003e728437be0f5f8021c10001be20ddd639c172bfccd9af
BLAKE2b-256 checksum
How to use checksums
f34b7291ca0090545dc6a7cd0dcf734655493ea994ee1e30e4bcd02c86a19d7a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp311-cp311-musllinux_1_2_aarch64.whl

Download URL anydist-1.0.1-cp311-cp311-musllinux_1_2_aarch64.whl
Size 2.3 MB
Tags CPython 3.11 Linux musl 1.2+ ARM64
SHA-256 checksum
How to use checksums
4f055c8a787b3e2d6bed8cf30feec2cad67e045f324eb744ea4c9e41bfb32278
BLAKE2b-256 checksum
How to use checksums
aeaf9cc3089ad21f9493dc23b9eed81c6f15f51d89a1bbf6cf26b0ef0c0f808d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL anydist-1.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
6377f39e3f0d7e5ec3e19da237ecea72231c9a31174201e97b815f39dcf02f92
BLAKE2b-256 checksum
How to use checksums
5e93f8ee615859cd55340d41263e11ce46418450b791827731b7525c0f48e8cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL anydist-1.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.3 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
097b9790d9dd64a4ecc4aafb798b26a17d647e969fbb3c5366093029fd0c7e0a
BLAKE2b-256 checksum
How to use checksums
d0fa72a4507f669d3213b87c4c1ecdc6de1d179c9aae413ad108086e45aaafb8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp311-cp311-macosx_15_0_arm64.whl

Download URL anydist-1.0.1-cp311-cp311-macosx_15_0_arm64.whl
Size 688.5 kB
Tags CPython 3.11 macOS 15.0+ ARM64
SHA-256 checksum
How to use checksums
5aaa946ccf619f90d8b07f295fb84fc16c8f51849c55c9c1296ef9fc3149f1d2
BLAKE2b-256 checksum
How to use checksums
0481b816a80cfa12d4ccd26925130bf90050eb017f25e6afad1b85b6351449d5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp310-cp310-musllinux_1_2_x86_64.whl

Download URL anydist-1.0.1-cp310-cp310-musllinux_1_2_x86_64.whl
Size 2.3 MB
Tags CPython 3.10 Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
63866858ace3c1de28ba7c46985de70c25aaf06b44bc3b08e91510d79e6e739a
BLAKE2b-256 checksum
How to use checksums
5fb5848a86eed341211cf3a5b922840e13f75ab9ea43242291e6236db22f11a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp310-cp310-musllinux_1_2_aarch64.whl

Download URL anydist-1.0.1-cp310-cp310-musllinux_1_2_aarch64.whl
Size 2.2 MB
Tags CPython 3.10 Linux musl 1.2+ ARM64
SHA-256 checksum
How to use checksums
00d9c6f969afe6fc9f9e7877dbcfa1563e24f777d4bdf3cbc46562fc4c1e200b
BLAKE2b-256 checksum
How to use checksums
3eda22bb8f34d1d484261cc16e13144762bc6a9bebb1f5939b50ff26d0c54468
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL anydist-1.0.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
003fa05075c535e6681c5564f59564f3d9066b7444d1b9f138f1fc89dd846b4a
BLAKE2b-256 checksum
How to use checksums
a648612ce57a03e92e13fdd724ff5116f92b07c7f52ba0c66d3cd69558539f4f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL anydist-1.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.2 MB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
542a61a8342b0d5a9f5af143cf6e4c58045484a93ba3254dfa429acf8aa74653
BLAKE2b-256 checksum
How to use checksums
03ffb974a3c6a20c3046dfa03dcda0fdbd61a3f76f472951f631b1da4cafbf32
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / anydist-1.0.1-cp310-cp310-macosx_15_0_arm64.whl

Download URL anydist-1.0.1-cp310-cp310-macosx_15_0_arm64.whl
Size 692.5 kB
Tags CPython 3.10 macOS 15.0+ ARM64
SHA-256 checksum
How to use checksums
2cf975f3b9fbda7f34444279f2faa2fd50642a0cee3ea902671a0280669f55c1
BLAKE2b-256 checksum
How to use checksums
6c38a2840a65dc7fbbab3dce1b4c391821474a59746e25020bae3d18da43ef84
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release history Release notifications | RSS feed

This release

1.0.1 This release

41 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page