diff --git a/bhv/abstract.py b/bhv/abstract.py index 6292317..71f92b8 100644 --- a/bhv/abstract.py +++ b/bhv/abstract.py @@ -90,17 +90,37 @@ def hamming(self, other: Self) -> int: return (self ^ other).active() def closest(self, vs: list[Self]) -> int: + """Return index of the vector that is closest in hamming distance to self""" return min(range(len(vs)), key=lambda i: self.hamming(vs[i])) + def furthest(self, vs: list[Self]) -> int: + """Return index of the vector that is furthest in hamming distance to self""" + return max(range(len(vs)), key=lambda i: self.hamming(vs[i])) + def top(self, vs: list[Self], k: int) -> list[int]: + """Return the indices of the k vectors that have the smallest hamming distance from self""" return list(sorted(range(len(vs)), key=lambda i: self.hamming(vs[i])))[:k] + def bottom(self, vs: list[Self], k: int) -> list[int]: + """Return the indices of the k vectors that have the biggest hamming distance from self""" + return list(sorted(range(len(vs)), key=lambda i: self.hamming(vs[i])))[(len(vs) - k):] + def within(self, vs: list[Self], d: int) -> list[int]: + """Return the indices of all the vectors with maximum hamming distance d to self (including distance d)""" return list(filter(lambda i: self.hamming(vs[i]) <= d, range(len(vs)))) + def outside(self, vs: list[Self], d: int) -> list[int]: + """Return the indices of all the vectors with minimum hamming distance d to self (excluding distance d)""" + return list(filter(lambda i: self.hamming(vs[i]) > d, range(len(vs)))) + def within_std(self, vs: list[Self], d: float, relative: bool = False) -> list[int]: + """Return the indices of all vectors that are less than d standard deviations away from self (including d)""" return self.within(vs, int(DIMENSION*(self.std_to_frac(d + self.EXPECTED_RAND_APART*relative)))) + def outside_std(self, vs: list[Self], d: float, relative: bool = False) -> list[int]: + """Return the indices of all vectors that are more than d standard deviations away from self (including d)""" + return self.outside(vs, int(DIMENSION*(self.std_to_frac(d + self.EXPECTED_RAND_APART*relative)))) + def distribution(self, vs: list[Self], metric=lambda x, y: x.std_apart(y), softmax: bool = False, base: int = e): ds = [metric(self, v) for v in vs] if softmax: diff --git a/tests/abstract.py b/tests/abstract.py new file mode 100644 index 0000000..24934b4 --- /dev/null +++ b/tests/abstract.py @@ -0,0 +1,58 @@ +import unittest + +from bhv.vanilla import VanillaBHV as BHV, DIMENSION + + +class BaseBHVMethods(unittest.TestCase): + def test_closest_furthest(self): + a, b, c, d = BHV.nrand(4) + + self.assertEqual(0, a.closest([a, b, c, d])) + + self.assertEqual(1, a.furthest([a, b])) + self.assertEqual(1, a.furthest([BHV.majority([a, b, c]), BHV.majority([a, b, c, d])])) + + def test_top_bottom(self): + a, b, c, d = BHV.nrand(4) + + self.assertEqual([0, 1], a.top([a, BHV.majority([a, b, c]), BHV.majority([a, b, c, d])], 2)) + self.assertEqual([1, 2], a.bottom([a, BHV.majority([a, b, c]), BHV.majority([a, b, c, d])], 2)) + + def test_within_outside(self): + a, b, c, d = BHV.nrand(4) + + self.assertEqual([0], a.within([a, b], 0)) + self.assertEqual([0, 1], a.within([a, b], DIMENSION)) + self.assertEqual([0], a.within([a ^ BHV.level(1)], DIMENSION)) + self.assertEqual([], a.within([a ^ BHV.level(1)], DIMENSION - 1)) + self.assertEqual([], a.within([a ^ BHV.level(.5)], DIMENSION/2 - 1)) + self.assertEqual([0], a.within([a ^ BHV.level(.5)], DIMENSION/2)) + self.assertEqual([0, 1], a.within([a, a ^ BHV.level(.001), a ^ BHV.level(.1), b], 100)) + + self.assertEqual([], a.outside([a], 0)) + self.assertEqual([], a.outside([b], DIMENSION)) + self.assertEqual([], a.outside([a ^ BHV.level(1)], DIMENSION)) + self.assertEqual([0], a.outside([a ^ BHV.level(1)], DIMENSION - 1)) + self.assertEqual([0], a.outside([a ^ BHV.level(.5)], DIMENSION / 2 - 1)) + self.assertEqual([], a.outside([a ^ BHV.level(.5)], DIMENSION / 2)) + self.assertEqual([2, 3], a.outside([a, a ^ BHV.level(.001), a ^ BHV.level(.1), b], 100)) + + vs = BHV.nrand(20) + for w in vs: + for a in range(0, DIMENSION, round(DIMENSION/100)): + self.assertSetEqual(set(w.outside(vs, a)), set(range(len(vs))) - set(w.within(vs, a))) + + def test_within_outside_std(self): + a, b, c, d = BHV.nrand(4) + + self.assertEqual([0, 1], a.within_std([a, b], a.std_apart(b))) + self.assertEqual([], a.outside_std([a, b], a.std_apart(b))) + + vs = BHV.nrand(20) + for w in vs: + for a in range(100): + self.assertSetEqual(set(w.outside_std(vs, a)), set(range(len(vs))) - set(w.within_std(vs, a))) + + +if __name__ == '__main__': + unittest.main()