chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
"""Tests for graphify/_minhash.py — MinHash sketch and band-LSH."""
|
||||
from __future__ import annotations
|
||||
import numpy as np
|
||||
import pytest
|
||||
from graphify._minhash import MinHash, MinHashLSH, _optimal_lsh_params
|
||||
|
||||
|
||||
def _minhash_for(text: str, num_perm: int = 128) -> MinHash:
|
||||
m = MinHash(num_perm=num_perm)
|
||||
for i in range(0, len(text) - 2):
|
||||
m.update(text[i:i + 3].encode())
|
||||
return m
|
||||
|
||||
|
||||
# ── MinHash ───────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_identical_texts_produce_identical_hashvalues():
|
||||
a = _minhash_for("graphextractor")
|
||||
b = _minhash_for("graphextractor")
|
||||
assert np.array_equal(a.hashvalues, b.hashvalues)
|
||||
|
||||
|
||||
def test_similar_texts_share_most_hashvalues():
|
||||
a = _minhash_for("authentication manager")
|
||||
b = _minhash_for("authentication managers")
|
||||
overlap = np.sum(a.hashvalues == b.hashvalues) / len(a.hashvalues)
|
||||
assert overlap > 0.5
|
||||
|
||||
|
||||
def test_unrelated_texts_share_few_hashvalues():
|
||||
a = _minhash_for("authentication manager")
|
||||
b = _minhash_for("file system watcher")
|
||||
overlap = np.sum(a.hashvalues == b.hashvalues) / len(a.hashvalues)
|
||||
assert overlap < 0.3
|
||||
|
||||
|
||||
def test_update_mutates_hashvalues():
|
||||
m = MinHash(num_perm=64)
|
||||
before = m.hashvalues.copy()
|
||||
m.update(b"hello")
|
||||
assert not np.array_equal(m.hashvalues, before)
|
||||
|
||||
|
||||
# ── MinHashLSH ────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_near_duplicates_are_candidates():
|
||||
lsh = MinHashLSH(threshold=0.5, num_perm=128)
|
||||
a = _minhash_for("authentication manager")
|
||||
b = _minhash_for("authentication managers")
|
||||
lsh.insert("a", a)
|
||||
lsh.insert("b", b)
|
||||
assert "b" in lsh.query(a)
|
||||
|
||||
|
||||
def test_unrelated_strings_not_candidates():
|
||||
lsh = MinHashLSH(threshold=0.5, num_perm=128)
|
||||
a = _minhash_for("authentication manager")
|
||||
b = _minhash_for("file system watcher")
|
||||
lsh.insert("a", a)
|
||||
lsh.insert("b", b)
|
||||
assert "b" not in lsh.query(a)
|
||||
|
||||
|
||||
def test_query_always_returns_self():
|
||||
lsh = MinHashLSH(threshold=0.5, num_perm=128)
|
||||
m = _minhash_for("graphextractor")
|
||||
lsh.insert("x", m)
|
||||
assert "x" in lsh.query(m)
|
||||
|
||||
|
||||
def test_duplicate_insert_raises():
|
||||
lsh = MinHashLSH(threshold=0.5, num_perm=128)
|
||||
m = _minhash_for("foo")
|
||||
lsh.insert("key", m)
|
||||
with pytest.raises(ValueError, match="already exists"):
|
||||
lsh.insert("key", m)
|
||||
|
||||
|
||||
# ── _optimal_lsh_params ───────────────────────────────────────────────────────
|
||||
|
||||
def test_optimal_params_within_budget():
|
||||
b, r = _optimal_lsh_params(0.5, 128)
|
||||
assert b >= 1 and r >= 1
|
||||
assert b * r <= 128
|
||||
|
||||
|
||||
def test_optimal_params_cached():
|
||||
result1 = _optimal_lsh_params(0.7, 128)
|
||||
result2 = _optimal_lsh_params(0.7, 128)
|
||||
assert result1 is result2
|
||||
|
||||
|
||||
# ── EDR regression: scipy / numpy.testing must not be imported ──────────────────
|
||||
|
||||
def test_dedup_import_does_not_pull_scipy_or_numpy_testing():
|
||||
import sys
|
||||
for mod in ("scipy", "numpy.testing"):
|
||||
sys.modules.pop(mod, None)
|
||||
import graphify.dedup # noqa: F401
|
||||
assert "scipy" not in sys.modules
|
||||
assert "numpy.testing" not in sys.modules
|
||||
Reference in New Issue
Block a user