From ad88306a06cddad295247c02317ca077d95051cc Mon Sep 17 00:00:00 2001 From: AnnelineD Date: Fri, 5 Jan 2024 17:26:31 +0100 Subject: [PATCH 1/3] Furthest, bottom, outside, outside_std methods --- bhv/abstract.py | 12 ++++++++++++ tests/abstract.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/abstract.py diff --git a/bhv/abstract.py b/bhv/abstract.py index 6292317..e723a5f 100644 --- a/bhv/abstract.py +++ b/bhv/abstract.py @@ -92,15 +92,27 @@ def hamming(self, other: Self) -> int: def closest(self, vs: list[Self]) -> int: return min(range(len(vs)), key=lambda i: self.hamming(vs[i])) + def furthest(self, vs: list[Self]) -> int: + return max(range(len(vs)), key=lambda i: self.hamming(vs[i])) + def top(self, vs: list[Self], k: int) -> list[int]: return list(sorted(range(len(vs)), key=lambda i: self.hamming(vs[i])))[:k] + def bottom_k(self, vs: list[Self], k: int) -> list[int]: + 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 list(filter(lambda i: self.hamming(vs[i]) <= d, range(len(vs)))) + def outside(self, vs: list[Self], d: int) -> list[int]: + 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 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 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..35db559 --- /dev/null +++ b/tests/abstract.py @@ -0,0 +1,48 @@ +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_k(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_k([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)) + + def test_within_outside_std(self): + a, b, c, d = BHV.nrand(4) + + self.assertEqual([0, 1], a.within([a, a ^ BHV.level(.0001), a ^ BHV.level(.01), b], 2)) + self.assertEqual([2, 3], a.outside([a, a ^ BHV.level(.0001), a ^ BHV.level(.1), b], 2)) + + +if __name__ == '__main__': + unittest.main() From c68e931b11907002b36d2ee02907f133de821370 Mon Sep 17 00:00:00 2001 From: AnnelineD Date: Fri, 12 Jan 2024 00:35:29 +0100 Subject: [PATCH 2/3] Add tests of within_std and outside_std --- tests/abstract.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/abstract.py b/tests/abstract.py index 35db559..24934b4 100644 --- a/tests/abstract.py +++ b/tests/abstract.py @@ -12,11 +12,11 @@ def test_closest_furthest(self): 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_k(self): + 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_k([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) @@ -37,11 +37,21 @@ def test_within_outside(self): 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([a, a ^ BHV.level(.0001), a ^ BHV.level(.01), b], 2)) - self.assertEqual([2, 3], a.outside([a, a ^ BHV.level(.0001), a ^ BHV.level(.1), b], 2)) + 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__': From ae5b1f6f8500a12a40f1e8cae9164a45f1163553 Mon Sep 17 00:00:00 2001 From: AnnelineD Date: Fri, 12 Jan 2024 00:40:00 +0100 Subject: [PATCH 3/3] bottom_k -> bottom, Documentation --- bhv/abstract.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bhv/abstract.py b/bhv/abstract.py index e723a5f..71f92b8 100644 --- a/bhv/abstract.py +++ b/bhv/abstract.py @@ -90,27 +90,35 @@ 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_k(self, vs: list[Self], k: int) -> list[int]: + 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):