From 3ffad4bf46b1309490ef6514393de741937713ef Mon Sep 17 00:00:00 2001 From: satej soman Date: Fri, 1 Mar 2019 15:04:16 -0600 Subject: [PATCH 1/6] set up harnesses to make sure np/builtins give same results as legacy utils --- graph/my_graph.py | 2 +- graph/my_graph_helpers.py | 31 +---- tests/legacy_util_tests.py | 200 ++++++++++++++++++++++++++++++ utils/__init__.py | 0 {graph => utils}/lazy_property.py | 0 5 files changed, 205 insertions(+), 28 deletions(-) create mode 100644 tests/legacy_util_tests.py create mode 100644 utils/__init__.py rename {graph => utils}/lazy_property.py (100%) diff --git a/graph/my_graph.py b/graph/my_graph.py index 765808f..b36c136 100644 --- a/graph/my_graph.py +++ b/graph/my_graph.py @@ -5,7 +5,7 @@ import warnings import json import my_graph_helpers as mgh -from lazy_property import lazy_property +from utils.lazy_property import lazy_property from matplotlib import pyplot as plt diff --git a/graph/my_graph_helpers.py b/graph/my_graph_helpers.py index c213b78..f6a7e7d 100644 --- a/graph/my_graph_helpers.py +++ b/graph/my_graph_helpers.py @@ -36,7 +36,8 @@ def distance(mynode0, mynode1): def distance_squared(mynode0, mynode1): - return (mynode0.x-mynode1.x)**2+(mynode0.y-mynode1.y)**2 + # https://stackoverflow.com/a/47775357 + return np.sum((a - b)**2 for (a, b) in zip(mynode0.loc, mynode1.loc)) def sq_distance_point_to_segment(target, myedge): @@ -168,32 +169,8 @@ def WeightedPick(d): """picks an item out of the dictionary d, with probability proportional to the value of that item. e.g. in {a:1, b:0.6, c:0.4} selects and returns "a" 5/10 times, "b" 3/10 times and "c" 2/10 times. """ - - r = random.uniform(0, sum(d.values())) - s = 0.0 - for k, w in d.items(): - s += w - if r < s: - return k - return k - - -def mat_reorder(matrix, order): - """sorts a square matrix so both rows and columns are - ordered by order. """ - - Drow = [matrix[i] for i in order] - Dcol = [[r[i] for i in order] for r in Drow] - - return Dcol - - -def myRoll(mylist): - """rolls a list, putting the last element into the first slot. """ - - mylist.insert(0, mylist[-1]) - del mylist[-1] - return(mylist) + sum_p = sum(d.values()) + return np.random.choice(d.keys(), p=[p/sum_p for p in d.values()]) ###################### # DUALS HElPER diff --git a/tests/legacy_util_tests.py b/tests/legacy_util_tests.py new file mode 100644 index 0000000..9bd3b6a --- /dev/null +++ b/tests/legacy_util_tests.py @@ -0,0 +1,200 @@ +import unittest +import numpy as np +import random +from graph import my_graph as mg +from graph import my_graph_helpers as mgh + +# myG geometry functions +def legacy_distance(mynode0, mynode1): + return np.sqrt(legacy_distance_squared(mynode0, mynode1)) + + +def legacy_distance_squared(mynode0, mynode1): + return (mynode0.x-mynode1.x)**2+(mynode0.y-mynode1.y)**2 + + +def legacy_sq_distance_point_to_segment(target, myedge): + """returns the square of the minimum distance between mynode + target and myedge. """ + n1 = myedge.nodes[0] + n2 = myedge.nodes[1] + + if myedge.length == 0: + sq_dist = distance_squared(target, n1) + elif target == n1 or target == n2: + sq_dist = 0 + else: + px = float(n2.x - n1.x) + py = float(n2.y - n1.y) + u = float((target.x - n1.x)*px + (target.y - n1.y)*py)/(px*px + py*py) + if u > 1: + u = 1 + elif u < 0: + u = 0 + x = n1.x + u*px + y = n1.y + u*py + + dx = x - target.x + dy = y - target.y + + sq_dist = (dx * dx + dy * dy) + return sq_dist + + +def legacy_intersect(e1, e2): + """ returns true if myedges e1 and e2 intersect """ + # fails for lines that perfectly overlap. + def legacy_ccw(a, b, c): + return (c.y-a.y)*(b.x-a.x) > (b.y-a.y)*(c.x-a.x) + + a = e1.nodes[0] + b = e1.nodes[1] + c = e2.nodes[0] + d = e2.nodes[1] + + return ccw(a, c, d) != ccw(b, c, d) and ccw(a, b, c) != ccw(a, b, d) + + +def legacy_are_parallel(e1, e2): + """ returns true if myedges e1 and e2 are parallel """ + a = e1.nodes[0] + b = e1.nodes[1] + c = e2.nodes[0] + d = e2.nodes[1] + + # check if parallel; handling divide by zero errors + if a.x == b.x and c.x == d.x: # check if both segments are flat + parallel = True + # if one is flat and other is not + elif (a.x - b.x)*(c.x - d.x) == 0 and (a.x - b.x) + (c.x - d.x) != 0: + parallel = False + # if neither segment is flat and slopes are equal + elif (a.y-b.y)/(a.x-b.x) == (c.y-d.y)/(c.x-d.x): + parallel = True + # n either segment is flat, slopes are not equal + else: + parallel = False + return parallel + + +def legacy_segment_distance_sq(e1, e2): + """returns the square of the minimum distance between myedges e1 and e2.""" + # check different + if e1 == e2: + sq_distance = 0 + # check parallel/colinear: + # lines are not parallel/colinear and intersect + if not are_parallel(e1, e2) and intersect(e1, e2): + sq_distance = 0 + # lines don't intersect, aren't parallel + else: + d1 = sq_distance_point_to_segment(e1.nodes[0], e2) + d2 = sq_distance_point_to_segment(e1.nodes[1], e2) + d3 = sq_distance_point_to_segment(e2.nodes[0], e1) + d4 = sq_distance_point_to_segment(e2.nodes[1], e1) + sq_distance = min(d1, d2, d3, d4) + + return sq_distance + + +# vector math +def legacy_bisect_angle(a, b, c, epsilon=0.2, radius=1): + """ finds point d such that bd bisects the lines ab and bc.""" + ax = a.x - b.x + ay = a.y - b.y + + cx = c.x - b.x + cy = c.y - b.y + + a1 = mg.MyNode(((ax, ay))/np.linalg.norm((ax, ay))) + c1 = mg.MyNode(((cx, cy))/np.linalg.norm((cx, cy))) + + # if vectors are close to parallel, find vector that is perpendicular to ab + # if they are not, then find the vector that bisects a and c + if abs(np.cross(a1.loc, c1.loc)) < 0 + epsilon: + # print("vectors {0}{1} and {1}{2} are close to //)".format(a,b,c) + dx = -ay + dy = ax + else: + dx = (a1.x + c1.x)/2 + dy = (a1.y + c1.y)/2 + + # convert d values into a vector of length radius + dscale = ((dx, dy)/np.linalg.norm((dx, dy)))*radius + myd = mg.MyNode(dscale) + + # make d a node in space, not vector around b + d = mg.MyNode((myd.x + b.x, myd.y + b.y)) + + return d + + +def legacy_find_negative(d, b): + """finds the vector -d when b is origin """ + negx = -1*(d.x - b.x) + b.x + negy = -1*(d.y - b.y) + b.y + dneg = mg.MyNode((negx, negy)) + return dneg + + +# clean up and probability functions +def legacy_WeightedPick(d): + """picks an item out of the dictionary d, with probability proportional to + the value of that item. e.g. in {a:1, b:0.6, c:0.4} selects and returns + "a" 5/10 times, "b" 3/10 times and "c" 2/10 times. """ + r = random.uniform(0, sum(d.values())) + s = 0.0 + for k, w in d.items(): + s += w + if r < s: + return k + return k + +class LegacyUtilTests(unittest.TestCase): + def test_distance(self): + node_a = mg.MyNode((0, 1)) + node_b = mg.MyNode((2, 3)) + + assert legacy_distance(node_a, node_b) == mgh.distance(node_a, node_b) + + def test_distance_squared(self): + node_a = mg.MyNode((0, 1)) + node_b = mg.MyNode((2, 3)) + + assert legacy_distance_squared(node_a, node_b) == mgh.distance_squared(node_a, node_b) + + def _test_sq_distance_point_to_segment(self): + pass + + def _test_intersect(self): + pass + + def _test_ccw(self): + pass + + def _test_are_parallel(self): + pass + + def _test_segment_distance_sq(self): + pass + + def _test_bisect_angle(self): + pass + + def _test_find_negative(self): + pass + + def test_WeightedPick(self): + distribution = {'a':1, 'b':0.6, 'c':0.4} + + random.seed(11235813) + legacy_value = legacy_WeightedPick(distribution) + + np.random.seed(11235813) + builtin_value = mgh.WeightedPick(distribution) + + assert legacy_value == builtin_value + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/graph/lazy_property.py b/utils/lazy_property.py similarity index 100% rename from graph/lazy_property.py rename to utils/lazy_property.py From a3c25fef0dee351b7adf629faf893bb92e2eb899 Mon Sep 17 00:00:00 2001 From: satej soman Date: Fri, 1 Mar 2019 23:32:22 -0600 Subject: [PATCH 2/6] revert distance fn --- graph/my_graph_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graph/my_graph_helpers.py b/graph/my_graph_helpers.py index f6a7e7d..030bc50 100644 --- a/graph/my_graph_helpers.py +++ b/graph/my_graph_helpers.py @@ -36,8 +36,7 @@ def distance(mynode0, mynode1): def distance_squared(mynode0, mynode1): - # https://stackoverflow.com/a/47775357 - return np.sum((a - b)**2 for (a, b) in zip(mynode0.loc, mynode1.loc)) + return (mynode0.x-mynode1.x)**2+(mynode0.y-mynode1.y)**2 def sq_distance_point_to_segment(target, myedge): @@ -445,6 +444,7 @@ def choose_path(myG, paths, alpha, strict_greedy=False): if strict_greedy is False: inv_weight = dict((k, 1.0/(paths[k]**alpha)) for k in paths) + print inv_weight target_path = WeightedPick(inv_weight) if strict_greedy is True: target_path = min(paths, key=paths.get) From cab0fc64edb74d9722e5ed82b40ac39810272625 Mon Sep 17 00:00:00 2001 From: satej soman Date: Fri, 1 Mar 2019 23:53:22 -0600 Subject: [PATCH 3/6] bump perf test runs --- tests/perf_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/perf_tests.py b/tests/perf_tests.py index 96684a5..6cfd9dd 100644 --- a/tests/perf_tests.py +++ b/tests/perf_tests.py @@ -27,7 +27,7 @@ def main(names): blocks = test_case("data/" + name, name) time_taken = timeit.timeit('test_case("data/" + name, name)', setup='from __main__ import test_case; name = "%s"' % name, - number=3) + number=10) print blocks, time_taken, blocks/time_taken if __name__ == '__main__': @@ -40,4 +40,4 @@ def main(names): 'NYC', 'Prague' ] - main(names) \ No newline at end of file + main(names) From 4a121ead1eb516be70b6c69f493456fe3140b672 Mon Sep 17 00:00:00 2001 From: satej soman Date: Tue, 5 Mar 2019 13:52:05 -0600 Subject: [PATCH 4/6] refactor length fn --- tests/perf_test_data | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/perf_test_data b/tests/perf_test_data index 864d5be..85f95b8 100644 --- a/tests/perf_test_data +++ b/tests/perf_test_data @@ -5,3 +5,10 @@ 1d11d71 NYC 1 0.142737150192 7.00588458333 1d11d71 Prague 2 0.0788838863373 25.353720422 1d11d71 CapeTown 3 63.7222030163 0.0470793515917 +e12af17 Phule_Nagar_v6 199 3.72162985802 53.4711961135 +e12af17 Epworth_Before 350 84.1490020752 4.15928877787 +e12af17 Epworth_demo 1 0.195746898651 5.10863777097 +e12af17 Las_Vegas 1 52.0890500546 0.0191978928192 +e12af17 NYC 1 0.135759830475 7.36594909188 +e12af17 Prague 2 0.0757038593292 26.4187323833 +e12af17 CapeTown 3 63.6627459526 0.0471233207916 \ No newline at end of file From 8b57289b30b59fd4322a7c3880c45876b81f4820 Mon Sep 17 00:00:00 2001 From: satej soman Date: Tue, 5 Mar 2019 18:50:38 -0600 Subject: [PATCH 5/6] inline weighted_pick --- graph/my_graph_helpers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/graph/my_graph_helpers.py b/graph/my_graph_helpers.py index 030bc50..053018f 100644 --- a/graph/my_graph_helpers.py +++ b/graph/my_graph_helpers.py @@ -443,9 +443,7 @@ def choose_path(myG, paths, alpha, strict_greedy=False): length more frequently """ if strict_greedy is False: - inv_weight = dict((k, 1.0/(paths[k]**alpha)) for k in paths) - print inv_weight - target_path = WeightedPick(inv_weight) + target_path = WeightedPick({path: length**-alpha for (path, length) in paths.items()}) if strict_greedy is True: target_path = min(paths, key=paths.get) From 780314c28d3b961832acbf0629ac4d0d2ce22575 Mon Sep 17 00:00:00 2001 From: satej soman Date: Tue, 5 Mar 2019 18:50:47 -0600 Subject: [PATCH 6/6] update perf test data --- tests/perf_test_data | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/perf_test_data b/tests/perf_test_data index 85f95b8..c8bcccf 100644 --- a/tests/perf_test_data +++ b/tests/perf_test_data @@ -11,4 +11,11 @@ e12af17 Epworth_demo 1 0.195746898651 5.10863777097 e12af17 Las_Vegas 1 52.0890500546 0.0191978928192 e12af17 NYC 1 0.135759830475 7.36594909188 e12af17 Prague 2 0.0757038593292 26.4187323833 -e12af17 CapeTown 3 63.6627459526 0.0471233207916 \ No newline at end of file +e12af17 CapeTown 3 63.6627459526 0.0471233207916 +4a121ea Phule_Nagar_v6 199 3.75716304779 52.9654948345 +4a121ea Epworth_Before 350 85.5207681656 4.09257315512 +4a121ea Epworth_demo 1 0.192051172256 5.20694556691 +4a121ea Las_Vegas 1 51.1493070126 0.0195506070054 +4a121ea NYC 1 0.129373073578 7.72958369423 +4a121ea Prague 2 0.0711929798126 28.0926575242 +4a121ea CapeTown 3 56.6484811306 0.052958171872 \ No newline at end of file