import json import random from time import time from collections import namedtuple import util from gift_wrapper import rapper from graham import graham_scan from mbc import mbc, mbc_no_shuffle, mbc2_no_shuffle, mbc2 from profile import Profiler from quick_hull import quick_hull import os.path #random.seed(1337_420) TimedResult = namedtuple("TimedResult", "algorithm points running_time") def time_it(f: callable, args: tuple = ()): start = time() f(*args) return str(time() - start) def initiate_file(file): with open(file, "w+") as tmp: tmp.write("algorithm\t\tpoints\t\ttime") def write_to_log(file, data): if not os.path.isfile(file): initiate_file(file) tmp = [] for res in data: line = str.join("\t\t", res) print(line) tmp.append(line) write_string = "\n" + str.join("\n", tmp) with open(file, "a+") as open_file: open_file.write(write_string) def calculate_hulls(number_of_points, points): return [TimedResult("graham", number_of_points, time_it(graham_scan, args=(points,))), TimedResult("gift", number_of_points, time_it(rapper, args=(points,))), TimedResult("quick", number_of_points, time_it(quick_hull, args=(points,))), TimedResult("mbch", number_of_points, time_it(mbc, args=(points,))), TimedResult("mbch2", number_of_points, time_it(mbc2, args=(points,)))] def do_square_tests(number_of_points): points_square = {util.gen_point(0, 100) for _ in range(number_of_points)} number_of_points = str(number_of_points) results = calculate_hulls(number_of_points, points_square) write_to_log("square_tests.log", results) def do_circular_tests(number_of_points): points_circular = {util.gen_point(0, 100) for _ in range(number_of_points)} results = calculate_hulls(number_of_points, points_circular) write_to_log("circular_tests.log", results) def do_triangular_tests(number_of_points): left, right, top = util.Point(1,1), util.Point(51,1), util.Point(26,40) points = {util.gen_triangular_point(left, right, top) for _ in range(number_of_points)} results = calculate_hulls(number_of_points, points) write_to_log("triangular_tests.log", results) def do_quadratic_tests(number_of_points): points = {util.gen_weird_point(-10, 10) for _ in range(number_of_points)} results = calculate_hulls(number_of_points, points) write_to_log("quadratic_tests.log", results) def sanity_check(): points = {util.gen_point(1, 50) for i in range(100)} graham = set(graham_scan(points)) gift = set(rapper(points)) quick = quick_hull(points) mbch = set.union(mbc(points)) mbch2 = set.union(mbc2(points)) assert gift == graham == quick == mbch == mbch2 def do_one_profile(num_points): print(f"==================================== PROFILE ({num_points}) ====================================") random.seed(6) points = {util.gen_point(0, 100) for _ in range(num_points)} tests = [ #("graham_scan", graham_scan), #("gift_wrapper", rapper), #("quick_hull", quick_hull), ("mbc", mbc), ("mbc2", mbc2), ("mbc_no_shuffle", mbc_no_shuffle), ("mbc2_no_shuffle", mbc2_no_shuffle), ] results = {} for algorithm, func in tests: Profiler.reset() func(points) times = dict(Profiler.results) print(f"-------------- {algorithm} --------------") print("Times:", times) total = times[algorithm] print("Total:", total) sum_profiled = sum(times.values()) - total print("Total Profiled:", sum_profiled) unaccounted = total - sum_profiled print("Unaccounted:", unaccounted) times["other"] = unaccounted results[algorithm] = { "times": times, "total": total, "total_profiled": sum_profiled, "unaccounted": unaccounted, } return results def do_profile(): num_points = 60_000 #results = do_one_profile(num_points) results = {"mbc": {"times": {"finding median": 0.006870746612548828, "partitioning set": 0.066436767578125, "flipping constraints": 0.13354206085205078, "shuffling constraints": 0.08272123336791992, "solving LP": 0.13545918464660645, "finding bridge points": 0.06052136421203613, "pruning between line points": 0.04519915580749512, "mbc": 0.5643129348754883, "other": 0.033562421798706055}, "total": 0.5643129348754883, "total_profiled": 0.5307505130767822, "unaccounted": 0.033562421798706055}, "mbc2": {"times": {"extra pruning step": 0.15866398811340332, "finding median": 0.00189971923828125, "partitioning set": 0.022511959075927734, "flipping constraints": 0.04117441177368164, "shuffling constraints": 0.030447006225585938, "solving LP": 0.05805325508117676, "finding bridge points": 0.025882959365844727, "pruning between line points": 0.014936208724975586, "mbc2": 0.3658268451690674, "other": 0.01225733757019043}, "total": 0.3658268451690674, "total_profiled": 0.35356950759887695, "unaccounted": 0.01225733757019043}, "mbc_no_shuffle": {"times": {"finding median": 0.006849050521850586, "partitioning set": 0.06539726257324219, "flipping constraints": 0.13605880737304688, "solving LP": 0.06955385208129883, "finding bridge points": 0.06419634819030762, "pruning between line points": 0.044390201568603516, "mbc_no_shuffle": 0.397723913192749, "other": 0.011278390884399414}, "total": 0.397723913192749, "total_profiled": 0.3864455223083496, "unaccounted": 0.011278390884399414}, "mbc2_no_shuffle": {"times": {"extra pruning step": 0.16416001319885254, "finding median": 0.002000570297241211, "partitioning set": 0.022954702377319336, "flipping constraints": 0.0455164909362793, "solving LP": 0.02709197998046875, "finding bridge points": 0.022936582565307617, "pruning between line points": 0.017188310623168945, "mbc2_no_shuffle": 0.30688953399658203, "other": 0.005040884017944336}, "total": 0.30688953399658203, "total_profiled": 0.3018486499786377, "unaccounted": 0.005040884017944336}} print("================== RESULTS ==================") print(json.dumps(results)) algorithms = list(results.keys()) steps = ( "other", "finding median", "partitioning set", "flipping constraints", "solving 2D", "finding bridge points", "pruning between line points", "shuffling constraints", "extra pruning step", ) data = [[result["times"].get(step, 0) * 1000 for result in results.values()] for step in steps] util.stacked_bar(data=data, series_labels=steps, category_labels=algorithms, show_values=True, value_format="{:.1f}", y_label="time (ms)", grid=False, reverse=False) if __name__ == '__main__': sanity_check() do_profile() exit() for i in range(50, 1000, 50): do_square_tests(i)