metta-faiss 0.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metta_faiss-0.9.1/PKG-INFO +33 -0
- metta_faiss-0.9.1/README.md +19 -0
- metta_faiss-0.9.1/metta_faiss.egg-info/PKG-INFO +33 -0
- metta_faiss-0.9.1/metta_faiss.egg-info/SOURCES.txt +10 -0
- metta_faiss-0.9.1/metta_faiss.egg-info/dependency_links.txt +1 -0
- metta_faiss-0.9.1/metta_faiss.egg-info/entry_points.txt +2 -0
- metta_faiss-0.9.1/metta_faiss.egg-info/requires.txt +3 -0
- metta_faiss-0.9.1/metta_faiss.egg-info/top_level.txt +1 -0
- metta_faiss-0.9.1/metta_faiss.py +86 -0
- metta_faiss-0.9.1/pyproject.toml +41 -0
- metta_faiss-0.9.1/setup.cfg +4 -0
- metta_faiss-0.9.1/tests/test_faiss.py +51 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: metta-faiss
|
|
3
|
+
Version: 0.9.1
|
|
4
|
+
Summary: faiss as a nearest-neighbour backend for a MeTTa embedding store
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: pymetta==0.9.1
|
|
12
|
+
Requires-Dist: faiss-cpu
|
|
13
|
+
Requires-Dist: numpy
|
|
14
|
+
|
|
15
|
+
# metta-faiss
|
|
16
|
+
|
|
17
|
+
faiss as a nearest-neighbour backend for a MeTTa embedding store.
|
|
18
|
+
|
|
19
|
+
`pymetta` names no third-party library. This package is one row against the `index` point, and the seat
|
|
20
|
+
finds it exactly as it finds a package written by somebody else: through the
|
|
21
|
+
`metta.extensions` entry point, loaded at the first dispatch that has no answer
|
|
22
|
+
without it, or through `import metta_faiss`, whose module body registers the same
|
|
23
|
+
rows.
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pip install metta-faiss # or: pip install 'pymetta[arrays]'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Its tests are in `tests/`, and they run in the workspace suite:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
sh extensions/python/test.sh ext/metta-faiss
|
|
33
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# metta-faiss
|
|
2
|
+
|
|
3
|
+
faiss as a nearest-neighbour backend for a MeTTa embedding store.
|
|
4
|
+
|
|
5
|
+
`pymetta` names no third-party library. This package is one row against the `index` point, and the seat
|
|
6
|
+
finds it exactly as it finds a package written by somebody else: through the
|
|
7
|
+
`metta.extensions` entry point, loaded at the first dispatch that has no answer
|
|
8
|
+
without it, or through `import metta_faiss`, whose module body registers the same
|
|
9
|
+
rows.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pip install metta-faiss # or: pip install 'pymetta[arrays]'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Its tests are in `tests/`, and they run in the workspace suite:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
sh extensions/python/test.sh ext/metta-faiss
|
|
19
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: metta-faiss
|
|
3
|
+
Version: 0.9.1
|
|
4
|
+
Summary: faiss as a nearest-neighbour backend for a MeTTa embedding store
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: pymetta==0.9.1
|
|
12
|
+
Requires-Dist: faiss-cpu
|
|
13
|
+
Requires-Dist: numpy
|
|
14
|
+
|
|
15
|
+
# metta-faiss
|
|
16
|
+
|
|
17
|
+
faiss as a nearest-neighbour backend for a MeTTa embedding store.
|
|
18
|
+
|
|
19
|
+
`pymetta` names no third-party library. This package is one row against the `index` point, and the seat
|
|
20
|
+
finds it exactly as it finds a package written by somebody else: through the
|
|
21
|
+
`metta.extensions` entry point, loaded at the first dispatch that has no answer
|
|
22
|
+
without it, or through `import metta_faiss`, whose module body registers the same
|
|
23
|
+
rows.
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pip install metta-faiss # or: pip install 'pymetta[arrays]'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Its tests are in `tests/`, and they run in the workspace suite:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
sh extensions/python/test.sh ext/metta-faiss
|
|
33
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
metta_faiss.py
|
|
3
|
+
pyproject.toml
|
|
4
|
+
metta_faiss.egg-info/PKG-INFO
|
|
5
|
+
metta_faiss.egg-info/SOURCES.txt
|
|
6
|
+
metta_faiss.egg-info/dependency_links.txt
|
|
7
|
+
metta_faiss.egg-info/entry_points.txt
|
|
8
|
+
metta_faiss.egg-info/requires.txt
|
|
9
|
+
metta_faiss.egg-info/top_level.txt
|
|
10
|
+
tests/test_faiss.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
metta_faiss
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Purpose: faiss as a nearest-neighbour backend of the MeTTa Python seat.
|
|
2
|
+
|
|
3
|
+
One row against the seat's `index` point, so an embedding store searches
|
|
4
|
+
through faiss when this package is installed. The row is NOT a fallback, which
|
|
5
|
+
is what makes `backend="auto"` prefer it over the generic Array API path
|
|
6
|
+
whatever order the two distributions loaded in; entry-point order is not
|
|
7
|
+
something a package can arrange.
|
|
8
|
+
|
|
9
|
+
NumPy is a dependency here and is not an integration: faiss' own interface
|
|
10
|
+
takes contiguous float32 NumPy arrays for `add` and `search`, so staging one is
|
|
11
|
+
part of calling faiss at all, and faiss-cpu depends on NumPy itself.
|
|
12
|
+
|
|
13
|
+
Assumes:
|
|
14
|
+
- a normalized matrix, which is the `index` point's contract: the backend
|
|
15
|
+
searches inner products and the store normalizes before it gets here
|
|
16
|
+
Guarantees:
|
|
17
|
+
- `available()` answers False rather than raising when faiss is absent, so
|
|
18
|
+
an embedding store falls through to the next backend instead of failing
|
|
19
|
+
[tested: tests/test_faiss.py::test_an_absent_faiss_declines_rather_than_raising;
|
|
20
|
+
commit=94057a0f073c0fab0a35c42beff2c324d8a0addd]
|
|
21
|
+
- faiss wins `backend="auto"` over the Array API fallback in either load
|
|
22
|
+
order [tested: tests/test_faiss.py::test_faiss_wins_auto_over_the_array_api_fallback;
|
|
23
|
+
commit=94057a0f073c0fab0a35c42beff2c324d8a0addd]
|
|
24
|
+
Open Obligations:
|
|
25
|
+
To Do: None
|
|
26
|
+
Hacks: None
|
|
27
|
+
Future Enhancements: None
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from typing import Any, Final
|
|
33
|
+
|
|
34
|
+
from metta import seam
|
|
35
|
+
|
|
36
|
+
_MISSING: Final = (
|
|
37
|
+
"the faiss embedding backend needs faiss-cpu; install pymetta[arrays]"
|
|
38
|
+
)
|
|
39
|
+
_NUMPY_MISSING: Final = (
|
|
40
|
+
"the faiss embedding backend stages its matrix as a contiguous float32 "
|
|
41
|
+
"NumPy array, which is faiss' own interface, and NumPy is not installed; "
|
|
42
|
+
"install pymetta[arrays]"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
require_module = seam.at("module").call()
|
|
46
|
+
optional_module = seam.at("optional-module").call()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _available() -> bool:
|
|
50
|
+
"""Whether faiss can run here."""
|
|
51
|
+
return optional_module("faiss") is not None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _build(matrix: Any) -> Any:
|
|
55
|
+
"""An exact inner-product index over the normalized matrix."""
|
|
56
|
+
faiss = require_module("faiss", _MISSING)
|
|
57
|
+
numpy = require_module("numpy", _NUMPY_MISSING)
|
|
58
|
+
staged = numpy.ascontiguousarray(numpy.asarray(matrix, dtype=numpy.float32))
|
|
59
|
+
index = faiss.IndexFlatIP(staged.shape[1])
|
|
60
|
+
index.add(staged)
|
|
61
|
+
return index
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _search(index: Any, query: Any, count: int) -> list[tuple[int, float]]:
|
|
65
|
+
"""(row, score) pairs best first, from the built index."""
|
|
66
|
+
numpy = require_module("numpy", _NUMPY_MISSING)
|
|
67
|
+
probe = numpy.ascontiguousarray(numpy.asarray(query, dtype=numpy.float32).reshape(1, -1))
|
|
68
|
+
scores, indexes = index.search(probe, count)
|
|
69
|
+
return [
|
|
70
|
+
(int(index), float(score))
|
|
71
|
+
for score, index in zip(scores[0], indexes[0], strict=True)
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def register() -> None:
|
|
76
|
+
"""This package's one row, against the seat's `index` point."""
|
|
77
|
+
seam.index.register(
|
|
78
|
+
"faiss",
|
|
79
|
+
available=_available,
|
|
80
|
+
build=_build,
|
|
81
|
+
search=_search,
|
|
82
|
+
missing=_MISSING,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
register()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Purpose: package metta-faiss, one distribution for one library.
|
|
2
|
+
# Assumes: pymetta of the same version is installed; this is a workspace member
|
|
3
|
+
# released with it and pinned to it exactly.
|
|
4
|
+
# Guarantees: importing metta_faiss registers this package's rows, and so does the
|
|
5
|
+
# `metta.extensions` entry point below when a dispatch discovers it.
|
|
6
|
+
# Decides: the dependency set is this library and nothing else, which is what
|
|
7
|
+
# makes "the core names no library" hold without an allowlist.
|
|
8
|
+
[build-system]
|
|
9
|
+
requires = ["setuptools>=83"]
|
|
10
|
+
build-backend = "setuptools.build_meta"
|
|
11
|
+
|
|
12
|
+
[project]
|
|
13
|
+
name = "metta-faiss"
|
|
14
|
+
version = "0.9.1"
|
|
15
|
+
description = "faiss as a nearest-neighbour backend for a MeTTa embedding store"
|
|
16
|
+
readme = "README.md"
|
|
17
|
+
requires-python = ">=3.12"
|
|
18
|
+
license = "MIT"
|
|
19
|
+
dependencies = [
|
|
20
|
+
#: pymetta of exactly this version. A member is released with the core and
|
|
21
|
+
#: reads its seam, so a floating pin would let a row meet a seam that no
|
|
22
|
+
#: longer declares its point.
|
|
23
|
+
"pymetta==0.9.1",
|
|
24
|
+
"faiss-cpu",
|
|
25
|
+
"numpy",
|
|
26
|
+
]
|
|
27
|
+
classifiers = [
|
|
28
|
+
"Development Status :: 3 - Alpha",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Intended Audience :: Science/Research",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
#: How a seat that never heard of this package finds it: one entry in the
|
|
34
|
+
#: group `metta.seam` reads, loaded at the first dispatch that has no answer
|
|
35
|
+
#: without it. The target is the MODULE, whose body registers on import, which
|
|
36
|
+
#: is the same door `import metta_faiss` opens.
|
|
37
|
+
[project.entry-points."metta.extensions"]
|
|
38
|
+
metta-faiss = "metta_faiss"
|
|
39
|
+
|
|
40
|
+
[tool.setuptools]
|
|
41
|
+
py-modules = ["metta_faiss"]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Purpose: prove the faiss row searches, and wins `backend="auto"`.
|
|
2
|
+
|
|
3
|
+
The second assertion is the load-bearing one. The Array API backend the array
|
|
4
|
+
layer registers is always available, so with both rows present the ORDER
|
|
5
|
+
decides `auto`, and order across distributions is not something a package can
|
|
6
|
+
arrange: the fallback flag is what makes the specific row win either way.
|
|
7
|
+
Open Obligations:
|
|
8
|
+
To Do: None
|
|
9
|
+
Hacks: None
|
|
10
|
+
Future Enhancements: None
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import metta_arrays
|
|
16
|
+
import metta_faiss
|
|
17
|
+
import metta_numpy # noqa: F401 -- the default array library the store needs
|
|
18
|
+
import pytest
|
|
19
|
+
|
|
20
|
+
from metta import S, seam
|
|
21
|
+
|
|
22
|
+
pytest.importorskip("faiss")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_the_row_is_registered_against_the_index_point():
|
|
26
|
+
"""One row, named for the library, and NOT a fallback."""
|
|
27
|
+
row = seam.index.find("faiss")
|
|
28
|
+
assert row is not None
|
|
29
|
+
assert row.available() is True
|
|
30
|
+
assert not row.fallback
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_faiss_wins_auto_over_the_array_api_fallback():
|
|
34
|
+
"""Every non-fallback row is consulted before every fallback one."""
|
|
35
|
+
names = [row.name for row in seam.index.rows()]
|
|
36
|
+
assert names.index("faiss") < names.index("argsort")
|
|
37
|
+
assert seam.index.find("argsort").fallback is True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_an_embedding_store_searches_through_faiss(scratch_space):
|
|
41
|
+
"""The store takes the backend by name and answers what faiss ranked."""
|
|
42
|
+
store = metta_arrays.EmbeddingStore(scratch_space, name="faiss_emb", backend="faiss")
|
|
43
|
+
store.add(S.dog, [1.0, 0.0])
|
|
44
|
+
store.add(S.cat, [0.0, 1.0])
|
|
45
|
+
assert [str(key) for key, _score in store.ranked([1.0, 0.0], 1)] == ["dog"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_an_absent_faiss_declines_rather_than_raising(monkeypatch):
|
|
49
|
+
"""available() answers False, so the store falls through to the next row."""
|
|
50
|
+
monkeypatch.setattr(metta_faiss, "optional_module", lambda _name: None)
|
|
51
|
+
assert seam.index.find("faiss").available() is False
|