def testinit(self): n = 15 P = [R1(i) for i in range(n)] M = MetricSpace(P) for j in range(n): for i in range(n - j): self.assertEqual(M.dist(P[i], P[i + j]), j)
def greedyHD(A: MetricSpace, B: MetricSpace): """ Compute Hausdorff distance after taking greedy permutations of A and B. """ A_g = MetricSpace(points = list(greedy(A)), dist = A.distfn, cache = A.cache, turnoffcache = A.turnoffcache) B_g = MetricSpace(points = list(greedy(B)), dist = B.distfn, cache = B.cache, turnoffcache = B.turnoffcache) return naiveHD(A_g, B_g)
def testinit_with_center(self): M = MetricSpace([Point([2 * i, 2 * i]) for i in range(100)]) MetricCell = Cell(MetricSpace()) C = MetricCell(Point([99, 99])) for p in M: C.addpoint(p) self.assertEqual(len(C), 101)
def testiter(self): """ The iteration order should match the insertion order. """ P = [R1(5), R1(2), R1(3)] M = MetricSpace(P) self.assertEqual(P, list(M)) newpoint = R1(4) M.add(newpoint) self.assertEqual(P + [R1(4)], list(M))
def testrebalance(self): a, b = Point([-1]), Point([200]) G = NeighborGraph(MetricSpace([a, b])) MetricCell = Cell(MetricSpace()) A = MetricCell(a) B = MetricCell(b) for i in range(200): B.addpoint(Point([i])) self.assertEqual(len(A), 1) self.assertEqual(len(B), 201) G.rebalance(A, B) self.assertEqual(len(B), 101) self.assertEqual(len(A), 101)
def test_points_on_a_line(): P = [Point([i]) for i in range(100)] GP = list(onehopgreedy(MetricSpace(P), P[50])) assert(GP[0] == P[50]) expected = [50, 16, 83] n = len(expected) assert(GP[:n] == [P[i] for i in expected])
def testgreedytree_transportplan_mass(self): """ This test determines that greedy() computes the correct transportation plan """ greedy = self.implementation.greedy root = Point([0]) P = MetricSpace([root] + [Point([x]) for x in [9, 3, 5, 18]]) #gp = greedy(P, root, tree=False, gettransportplan = True) gp = greedy(P, root, tree=False, gettransportplan=True, mass=[2, 2, 2, 2, 2]) # A case-wise assertion was needed because the way clarksongreedy.greedy works, # it looks into all neighbors which might have been changed when a new cell was # created. So the transportation plan has entries in the dictionary with 0 mass # moved. On the other hand quadraticgreedy.greedy stores points in the # transportation plan only if their reverse nearest neighbor changed. # So there are no 0 value keys here. if self.implementation == clarksongreedy: self.assertEqual(next(gp), (Point([0]), {Point([0]): 10})) self.assertEqual(next(gp), (Point([18]), { Point([0]): -2, Point([18]): 2 })) self.assertEqual(next(gp), (Point([9]), { Point([9]): 4, Point([0]): -4, Point([18]): 0 })) self.assertEqual(next(gp), (Point([5]), { Point([9]): -2, Point([0]): -2, Point([5]): 4 })) self.assertEqual(next(gp), (Point([3]), { Point([9]): 0, Point([3]): 2, Point([5]): -2 })) else: self.assertEqual(next(gp), (Point([0]), {Point([0]): 10})) self.assertEqual(next(gp), (Point([18]), { Point([0]): -2, Point([18]): 2 })) self.assertEqual(next(gp), (Point([9]), { Point([9]): 4, Point([0]): -4 })) self.assertEqual(next(gp), (Point([5]), { Point([9]): -2, Point([0]): -2, Point([5]): 4 })) self.assertEqual(next(gp), (Point([3]), { Point([3]): 2, Point([5]): -2 }))
def test_smallinstance(self): k = 11 # S = set(range(0, 100, 4)) S = set(range(0, 100, 4)) | set(range(100, 200, 10)) P = [Point([c]) for c in S] M = MetricSpace(P) output = list(knnsample(M, k, P[0])) print(len(output), output) self.assertTrue(len(output) >= len(S) / k)
def testgreedy_exponential_example(self): greedy = self.implementation.greedy P = [Point([(-3)**i]) for i in range(100)] # print([str(p) for p in P]) M = MetricSpace(P) gp = greedy(M, P[0]) self.assertEqual(next(gp), P[0]) self.assertEqual(next(gp), P[99]) for i in range(98, 0, -1): self.assertEqual(next(gp), P[i])
def testaddpoint(self): a, b, c = Point([1, 2]), Point([2, 3]), Point([3, 4]) MetricCell = Cell(MetricSpace()) C = MetricCell(a) self.assertEqual(len(C), 1) C.addpoint(b) self.assertEqual(len(C), 2) C.addpoint(c) self.assertEqual(len(C), 3) self.assertEqual(C.points, {a, b, c})
def test_getitem_subspace(self): def dist(x: float, y: float): return abs(x - y) a = 1 b = 5 c = 9 M = MetricSpace([a, b, c], dist) self.assertEqual(M[0], 1) M_1 = M[1:] self.assertEqual(M_1.dist(M_1[0], M_1[1]), 4)
def testcaching(self): class TestPoint: def __init__(self): self.distances_computed = set() def dist(self, other): assert ((self, other) not in self.distances_computed) self.distances_computed.add((self, other)) return 0 a, b, c = TestPoint(), TestPoint(), TestPoint() M = MetricSpace([a, b]) self.assertEqual(M.dist(a, b), 0) with self.assertRaises(AssertionError): a.dist(b) # Reversing operands also hits the cache. self.assertEqual(M.dist(b, a), 0) # Other distances can still be computed. self.assertEqual(a.dist(c), 0) self.assertEqual(b.dist(c), 0)
def testgreedytree_example2(self): greedy = self.implementation.greedy P = [Point([c]) for c in [0, 100, 49, 25, 60, 12, 81]] M = MetricSpace(P) root = P[0] gt = list(greedy(M, root, tree=True)) gp = [p for p, i in gt] ch = defaultdict(list) for p, i in greedy(M, root, tree=True): if i is not None: ch[gp[i]].append(p) self.assertEqual(gp, [P[i] for i in [0, 1, 2, 3, 6, 5, 4]])
def testpop(self): a, b, c, d = Point([0, 0]), Point([100, 0]), Point([0, 50]), Point([25, 25]) MetricCell = Cell(MetricSpace()) C = MetricCell(a) C.addpoint(b) C.addpoint(c) C.addpoint(d) self.assertEqual(C.pop(), b) self.assertEqual(C.pop(), c) self.assertEqual(C.pop(), d) self.assertEqual(C.pop(), None)
def testgreedytree_randomexample(self): greedy = self.implementation.greedy root = Point([0]) P = MetricSpace([root] + [Point([x]) for x in [8, 12, 100, 40, 70, 1, 72]]) gp = greedy(P, root, tree=True) self.assertEqual(next(gp), (Point([0]), None)) self.assertEqual(next(gp), (Point([100]), 0)) # radius = 100 self.assertEqual(next(gp), (Point([40]), 0)) self.assertEqual(next(gp), (Point([70]), 1)) self.assertEqual(next(gp), (Point([12]), 0)) self.assertEqual(next(gp), (Point([8]), 4)) self.assertEqual(next(gp), (Point([72]), 3))
def testaddpoint_duplicatepoint(self): a, b = Point([1, 2]), Point([2, 3]) MetricCell = Cell(MetricSpace()) C = MetricCell(a) self.assertEqual(len(C), 1) C.addpoint(b) self.assertEqual(len(C), 2) # Add b a second time. C.addpoint(b) self.assertEqual(len(C), 2) self.assertEqual(C.points, {a, b}) # Add the center again. C.addpoint(a) self.assertEqual(len(C), 2) self.assertEqual(C.points, {a, b})
def testgreedytree_bigexample(self): greedy = self.implementation.greedy n = 600 coords = set() while len(coords) < n: coords.add((randrange(100), randrange(100), randrange(100))) P = [Point(c) for c in coords] M = MetricSpace(P) GP = list(greedy(M, P[0], tree=True)) radii = [p.dist(GP[i][0]) for p, i in GP if i is not None] # Check that the insertion radii are nonincreasing. for i in range(n - 2): self.assertTrue(radii[i] >= radii[i + 1], str([i, radii[i], radii[i + 1]]))
def naiveDirectedHD(A: MetricSpace, B: MetricSpace, cmax: float = 0): """ Compute directed Hausdorff distance naively in O(n^2) time. """ for a in A: cmin = float('inf') cont = True for b in B: d = A.distfn(a,b) if d < cmax: cont = False break if d < cmin: cmin = d if cont and cmin > cmax: cmax = cmin return cmax
def testgreedytree_example3(self): greedy = self.implementation.greedy P = [Point([c]) for c in [0, 1, 3, 5, 20, 30]] M = MetricSpace(P) gt = list(greedy(M, P[0], tree=True)) gp = [p for p, i in gt] ch = defaultdict(set) for p, i in gt: if i is not None: ch[gp[i]].add(p) self.assertEqual(gp, [P[0], P[5], P[4], P[3], P[2], P[1]]) self.assertEqual(ch[P[0]], {P[5], P[3], P[1]}) self.assertEqual(ch[P[1]], set()) self.assertEqual(ch[P[2]], set()) self.assertEqual(ch[P[3]], {P[2]}) self.assertEqual(ch[P[4]], set()) self.assertEqual(ch[P[5]], {P[4]})
def test_dist_and_distsq(self): class TestPoint(float): def dist(self, other): return 2 * abs(self - other) a = TestPoint(4) b = TestPoint(5) c = TestPoint(9) M = MetricSpace([a, b, c]) self.assertEqual(M.dist(a, b), 2) self.assertEqual(M.dist(a, c), 10) self.assertEqual(M.dist(c, a), 10) self.assertEqual(M.dist(a, b), 2) # still self.assertEqual(M.dist(c, b), 8) self.assertEqual(M.distsq(a, b), 4) self.assertEqual(M.distsq(a, c), 100) self.assertEqual(M.distsq(c, a), 100) self.assertEqual(M.distsq(a, b), 4) # still self.assertEqual(M.distsq(c, b), 64)
def testneighborsofneighborscondition(self): """ This somewhat long test was written to expose a bug where neighbors of the neighbor graph were not properly discovered. The construction highlights the need for the neighbor graph to be undirected. """ a = L_inf([0, 2, 21, 11, 22, 19]) aa = L_inf([2, 0, 19, 9, 20, 17]) b = L_inf([21, 19, 0, 10, 21, 18]) bb = L_inf([11, 9, 10, 0, 11, 8]) c = L_inf([22, 20, 21, 11, 0, 3]) cc = L_inf([19, 17, 18, 8, 3, 0]) P = [a, aa, b, bb, c, cc] G = NeighborGraph(MetricSpace(P)) self.assertEqual(len(G), 1) p = G.heap.findmax() self.assertEqual(p.center, a) self.assertEqual(p.pop(), c) G.addcell(c, p) self.assertEqual(len(G), 2) p = G.heap.findmax() self.assertEqual(p.pop(), b) G.addcell(b, p) V = {v.center: v for v in G.vertices()} self.assertEqual(set(V), {a, b, c}) self.assertTrue(V[b] in G.nbrs(V[a])) self.assertTrue(V[a] in G.nbrs(V[b])) self.assertTrue(V[b] in G.nbrs(V[c])) self.assertTrue(V[c] in G.nbrs(V[b])) # Before adding aa. self.assertTrue(bb in V[b]) self.assertEqual(V[a].pop(), aa) G.addcell(aa, V[a]) V = {v.center: v for v in G.vertices()} # After adding aa. self.assertTrue(cc in V[c]) self.assertTrue(bb in V[aa]) self.assertTrue(bb.dist(cc) < bb.dist(aa)) # That means that we should have an edge from c to aa. self.assertTrue(V[aa] in G.nbrs(V[c]))
def testdist(self): MetricCell = Cell(MetricSpace()) A = MetricCell(Point([2, 3])) self.assertEqual(A.dist(Point([7, 3])), 5) self.assertEqual(A.dist(A.center), 0) self.assertEqual(A.dist(Point([7, 15])), 13)
def testupdateradius_empty_cell(self): MetricCell = Cell(MetricSpace()) C = MetricCell(Point([1, 2, 3])) C.updateradius() self.assertEqual(C.radius, 0)
def naiveHD(A: MetricSpace, B: MetricSpace, cmax: float = 0): """ Compute the Hausdorff distance naively using 2 calls to naiveDirectedHD """ return max(naiveDirectedHD(A, B, cmax), naiveDirectedHD(B, A, cmax)) def greedyHD(A: MetricSpace, B: MetricSpace): """ Compute Hausdorff distance after taking greedy permutations of A and B. """ A_g = MetricSpace(points = list(greedy(A)), dist = A.distfn, cache = A.cache, turnoffcache = A.turnoffcache) B_g = MetricSpace(points = list(greedy(B)), dist = B.distfn, cache = B.cache, turnoffcache = B.turnoffcache) return naiveHD(A_g, B_g) def l_inf(p: Point, q: Point): return max(abs(p[0] - q[0]), abs(p[1] - q[1])) M = 300 N = 350 seed(0) points = [Point([randrange(5, M-5), randrange(5,M-5)]) for i in range(N)] points = list(dict.fromkeys(points)) X = MetricSpace(points = points, dist=l_inf) A = X[:N//2] B = X[N//2:] print(naiveHD(A, B)) print(greedyHD(A, B))
def testinit(self): n = 9 P = [Point((i, 0, j, 0)) for i in range(n) for j in range(n)] M = MetricSpace(P) MDS(M)
def setUp(self): self.P = [Point([c]) for c in [0, 100, 49, 25, 60, 12, 81]] self.M = MetricSpace(self.P)
def testinit_empty(self): M = MetricSpace()
def testbasicusage(self): G = NeighborGraph(MetricSpace([Point([i, i]) for i in range(100)]))
def metricspace(self, target_dimension=False): n = len(self.M) return MetricSpace([NumpyPoint(self.Q[i]) for i in range(n)])
def testgreedy(self): greedy = self.implementation.greedy P = [Point([i]) for i in range(3)] M = MetricSpace(P) gp = list(greedy(M, P[0])) self.assertEqual(gp, [P[0], P[2], P[1]])