mirror of
https://github.com/facebookresearch/faiss.git
synced 2026-10-11 22:50:00 +00:00
Summary: Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5444 We fix every remaining flake8 E501 (line > 80 chars) violation in fbcode/faiss/ (excluding gpu/), closing out a lint-cleanup initiative that a June 29, 2026 diff stack (9a05463576 -> d304e29b9a -> 7d140d86ca) explicitly promised but left 41% incomplete. The middle diff in that stack aligned .flake8's max-line-length to black's 80-char width and stated the remaining 277 violations after the config change were "genuine lint (E501 long strings/URLs, F401/F841/B011, etc.), addressed in later commits in this stack." The follow-up commit fixed only the F401/F403/F841/B011 categories (45 issues) and never touched E501, leaving 192 violations across 53 files live in trunk. A direct re-scan today found 201 violations across 54 files (two files gained lines since the original count). We fixed 183 violations across 52 files. Two of the originally-flagged files, benchs/bench_rabitq.py and demos/index_pq_flat_separate_codes_from_codebook.py, already carry a file-level `# flake8: noqa` directive (the second is a Bento/Grimaldi notebook export that also fails standalone py_compile due to embedded shell magic) -- flake8 ignores both files entirely, so no changes were needed there and they are excluded from this diff. Fix approach, by category: - The large majority of violations are prose: docstrings and comments rewrapped at word boundaries, preserving each file's existing indentation and docstring conventions. No wording was changed. - Code lines (function calls, f-strings, assert messages, long argument lists) were reflowed in black-compatible style: parenthesization, comma breaks, or implicit adjacent string-literal concatenation. Every touched file is confirmed unchanged by `black --line-length 80 --diff` after the edit. - A small number of genuinely unwrappable long URLs (no reasonable break point) were suppressed with `# noqa: E501` rather than force-wrapped, matching the precedent set by the original black-format diff stack. - demos/offline_ivf/offline_ivf.py: a repeated, non-trivial ratio expression used in both an assert condition and its (lazily-evaluated) failure message was hoisted into a single local variable, eliminating the duplicate computation. The expression is pure and deterministic (built from already-computed arrays), so this is behaviorally identical to the original -- not a functional change. - `arc lint -a` additionally auto-fixed a real bug it detected while processing these files: `raise NotImplemented` -> `raise NotImplementedError` in contrib/evaluation.py (the sentinel object NotImplemented is not an exception type; raising it produces a TypeError masking the intended NotImplementedError). It also added a missing trailing newline in benchs/fb/bench_index_binary_from_float.py and, in the same file, rewrote the `#!/usr/bin/env python2` shebang to `#!/usr/bin/env fbpython` -- that rewrite was reverted by hand, since the file still does `import cPickle` (a Python 2-only module), so the auto-fix would have made the shebang describe an interpreter the script cannot actually run under. Fixing the underlying py2-to-py3 migration is out of scope for this line-length-only sweep. - Rebasing onto master (this diff's base commit had drifted) surfaced two concurrent upstream fixes to `check_ref_knn_with_draws(...)` calls in tests/test_ivf_flat_panorama.py and tests/test_refine_panorama.py (an unrelated in-flight correctness fix passing `D_panorama`/`atol` instead of a stale `D_regular` arg with no `atol`). The rebase took upstream's corrected arguments and re-applied only this diff's line-wrap on top. We verified zero functional change with an AST-level check: every string and f-string literal in every touched file was extracted before and after the edit and compared with whitespace normalized, confirming no wording, value, or expression was altered beyond the intended `# noqa: E501` suffixes and the one documented ratio-hoisting simplification above. Reviewed By: mnorris11 Differential Revision: D112525458 fbshipit-source-id: e4b85d73dafcaac779f3522a383a66e490fa942a
192 lines
4.3 KiB
Python
Executable File
192 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env fbpython
|
|
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
#
|
|
# This source code is licensed under the MIT license found in the
|
|
# LICENSE file in the root directory of this source tree.
|
|
|
|
from __future__ import print_function
|
|
import time
|
|
import numpy as np
|
|
|
|
try:
|
|
import matplotlib
|
|
|
|
matplotlib.use("Agg")
|
|
from matplotlib import pyplot
|
|
|
|
graphical_output = True
|
|
except ImportError:
|
|
graphical_output = False
|
|
|
|
import faiss
|
|
|
|
#################################################################
|
|
# Small I/O functions
|
|
#################################################################
|
|
|
|
|
|
def ivecs_read(fname):
|
|
a = np.fromfile(fname, dtype="int32")
|
|
d = a[0]
|
|
return a.reshape(-1, d + 1)[:, 1:].copy()
|
|
|
|
|
|
def fvecs_read(fname):
|
|
return ivecs_read(fname).view("float32")
|
|
|
|
|
|
def plot_OperatingPoints(ops, nq, **kwargs):
|
|
ops = ops.optimal_pts
|
|
n = ops.size() * 2 - 1
|
|
pyplot.plot(
|
|
[ops.at(i // 2).perf for i in range(n)],
|
|
[ops.at((i + 1) // 2).t / nq * 1000 for i in range(n)],
|
|
**kwargs
|
|
)
|
|
|
|
|
|
#################################################################
|
|
# prepare common data for all indexes
|
|
#################################################################
|
|
|
|
|
|
t0 = time.time()
|
|
|
|
print("load data")
|
|
|
|
xt = fvecs_read("sift1M/sift_learn.fvecs")
|
|
xb = fvecs_read("sift1M/sift_base.fvecs")
|
|
xq = fvecs_read("sift1M/sift_query.fvecs")
|
|
|
|
d = xt.shape[1]
|
|
|
|
print("load GT")
|
|
|
|
gt = ivecs_read("sift1M/sift_groundtruth.ivecs")
|
|
gt = gt.astype("int64")
|
|
k = gt.shape[1]
|
|
|
|
print("prepare criterion")
|
|
|
|
# criterion = 1-recall at 1
|
|
crit = faiss.OneRecallAtRCriterion(xq.shape[0], 1)
|
|
crit.set_groundtruth(None, gt)
|
|
crit.nnn = k
|
|
|
|
# indexes that are useful when there is no limitation on memory usage
|
|
unlimited_mem_keys = [
|
|
"IMI2x10,Flat",
|
|
"IMI2x11,Flat",
|
|
"IVF4096,Flat",
|
|
"IVF16384,Flat",
|
|
"PCA64,IMI2x10,Flat",
|
|
]
|
|
|
|
# memory limited to 16 bytes / vector
|
|
keys_mem_16 = [
|
|
"IMI2x10,PQ16",
|
|
"IVF4096,PQ16",
|
|
"IMI2x10,PQ8+8",
|
|
"OPQ16_64,IMI2x10,PQ16",
|
|
]
|
|
|
|
# limited to 32 bytes / vector
|
|
keys_mem_32 = [
|
|
"IMI2x10,PQ32",
|
|
"IVF4096,PQ32",
|
|
"IVF16384,PQ32",
|
|
"IMI2x10,PQ16+16",
|
|
"OPQ32,IVF4096,PQ32",
|
|
"IVF4096,PQ16+16",
|
|
"OPQ16,IMI2x10,PQ16+16",
|
|
]
|
|
|
|
# indexes that can run on the GPU
|
|
keys_gpu = [
|
|
"PCA64,IVF4096,Flat",
|
|
"PCA64,Flat",
|
|
"Flat",
|
|
"IVF4096,Flat",
|
|
"IVF16384,Flat",
|
|
"IVF4096,PQ32",
|
|
]
|
|
|
|
|
|
keys_to_test = unlimited_mem_keys
|
|
use_gpu = False
|
|
|
|
|
|
if use_gpu:
|
|
# if this fails, it means that the GPU version was not compiled
|
|
assert faiss.StandardGpuResources, (
|
|
"Faiss was not compiled with GPU support, "
|
|
"or loading _swigfaiss_gpu.so failed"
|
|
)
|
|
res = faiss.StandardGpuResources()
|
|
dev_no = 0
|
|
|
|
# remember results from other index types
|
|
op_per_key = []
|
|
|
|
|
|
# keep track of optimal operating points seen so far
|
|
op = faiss.OperatingPoints()
|
|
|
|
|
|
for index_key in keys_to_test:
|
|
|
|
print("============ key", index_key)
|
|
|
|
# make the index described by the key
|
|
index = faiss.index_factory(d, index_key)
|
|
|
|
if use_gpu:
|
|
# transfer to GPU (may be partial)
|
|
index = faiss.index_cpu_to_gpu(res, dev_no, index)
|
|
params = faiss.GpuParameterSpace()
|
|
else:
|
|
params = faiss.ParameterSpace()
|
|
|
|
params.initialize(index)
|
|
|
|
print("[%.3f s] train & add" % (time.time() - t0))
|
|
|
|
index.train(xt)
|
|
index.add(xb)
|
|
|
|
print("[%.3f s] explore op points" % (time.time() - t0))
|
|
|
|
# find operating points for this index
|
|
opi = params.explore(index, xq, crit)
|
|
|
|
print("[%.3f s] result operating points:" % (time.time() - t0))
|
|
opi.display()
|
|
|
|
# update best operating points so far
|
|
op.merge_with(opi, index_key + " ")
|
|
|
|
op_per_key.append((index_key, opi))
|
|
|
|
if graphical_output:
|
|
# graphical output (to tmp/ subdirectory)
|
|
|
|
fig = pyplot.figure(figsize=(12, 9))
|
|
pyplot.xlabel("1-recall at 1")
|
|
pyplot.ylabel(
|
|
"search time (ms/query, %d threads)" % faiss.omp_get_max_threads()
|
|
)
|
|
pyplot.gca().set_yscale("log")
|
|
pyplot.grid()
|
|
for i2, opi2 in op_per_key:
|
|
plot_OperatingPoints(opi2, crit.nq, label=i2, marker="o")
|
|
# plot_OperatingPoints(
|
|
# op, crit.nq, label = 'best', marker = 'o', color = 'r'
|
|
# )
|
|
pyplot.legend(loc=2)
|
|
fig.savefig("tmp/demo_auto_tune.png")
|
|
|
|
|
|
print("[%.3f s] final result:" % (time.time() - t0))
|
|
|
|
op.display()
|