From b6dadc7789cc95497e8b7618f666eac0734659d7 Mon Sep 17 00:00:00 2001 From: "Casper V. Kristensen" Date: Sun, 21 Oct 2018 16:59:17 +0200 Subject: [PATCH] Matplotlib profiling of MBC. --- h2/mbc.py | 6 ++-- h2/tmptest.py | 78 ++++++++++++--------------------------------------- h2/util.py | 64 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 63 deletions(-) diff --git a/h2/mbc.py b/h2/mbc.py index c29ae65..7ead310 100644 --- a/h2/mbc.py +++ b/h2/mbc.py @@ -14,7 +14,6 @@ def sidedness(slope: float, intersection: float, p3: Point, flipper: callable, e return Side.BELOW -@Profiler("solving 1D LP") def solve_1dlp(c, constraints): c1, c2 = c ((a1, a2), b) = constraints[-1] @@ -36,7 +35,7 @@ def solve_1dlp(c, constraints): return interval[1], q - (p * interval[1]) -@Profiler("solving 2D LP") +@Profiler("solving LP") def solve_2dlp(c, constraints): c1, c2 = c x1 = -10_000 if c1 > 0 else 10_000 @@ -88,7 +87,8 @@ def mbc_ch(points: Set[Point], flipper: callable, extra_prune=False, shuffle=Tru pr = {p for p in points if p.x >= med_x} # Shuffle - constraints = [((flipper(-p.x), flipper(-1)), flipper(-p.y)) for p in points] + with Profiler("flipping constraints"): + constraints = [((flipper(-p.x), flipper(-1)), flipper(-p.y)) for p in points] if shuffle: with Profiler("shuffling constraints"): random.shuffle(constraints) diff --git a/h2/tmptest.py b/h2/tmptest.py index f3e8584..0d722e9 100644 --- a/h2/tmptest.py +++ b/h2/tmptest.py @@ -123,6 +123,7 @@ def do_one_profile(num_points): unaccounted = total - sum_profiled print("Unaccounted:", unaccounted) + times["other"] = unaccounted results[algorithm] = { "times": times, @@ -135,80 +136,37 @@ def do_one_profile(num_points): def do_profile(): - import pandas as pd - import numpy as np - import altair as alt - - ### GATHER DATA ### - - #results = {num_points: do_one_profile(num_points) for num_points in (10_000, 30_000, 60_000)} - results = {"10000": {"mbc": {"times": {"finding median": 0.0010907649993896484, "partitioning set": 0.00837850570678711, "shuffling constraints": 0.01737236976623535, "solving 1D LP": 0.018637895584106445, "solving 2D LP": 0.023772716522216797, "finding bridge points": 0.00942373275756836, "pruning between line points": 0.0062139034271240234, "mbc": 0.08948898315429688}, "total": 0.08948898315429688, "total_profiled": 0.08488988876342773, "unaccounted": 0.004599094390869141}, "mbc2": {"times": {"extra pruning step": 0.021185636520385742, "finding median": 0.00042319297790527344, "partitioning set": 0.002390623092651367, "shuffling constraints": 0.00530552864074707, "solving 1D LP": 0.002721548080444336, "solving 2D LP": 0.004323244094848633, "finding bridge points": 0.0038061141967773438, "pruning between line points": 0.0017795562744140625, "mbc2": 0.0463566780090332}, "total": 0.0463566780090332, "total_profiled": 0.04193544387817383, "unaccounted": 0.004421234130859375}, "mbc_no_shuffle": {"times": {"finding median": 0.0011930465698242188, "partitioning set": 0.008675098419189453, "solving 1D LP": 0.012013912200927734, "solving 2D LP": 0.016475915908813477, "finding bridge points": 0.009999275207519531, "pruning between line points": 0.006704092025756836, "mbc_no_shuffle": 0.0634622573852539}, "total": 0.0634622573852539, "total_profiled": 0.05506134033203125, "unaccounted": 0.008400917053222656}, "mbc2_no_shuffle": {"times": {"extra pruning step": 0.020714282989501953, "finding median": 0.0004355907440185547, "partitioning set": 0.0022919178009033203, "solving 1D LP": 0.004298210144042969, "solving 2D LP": 0.005736112594604492, "finding bridge points": 0.004106044769287109, "pruning between line points": 0.0017347335815429688, "mbc2_no_shuffle": 0.04152178764343262}, "total": 0.04152178764343262, "total_profiled": 0.03931689262390137, "unaccounted": 0.00220489501953125}}, "30000": {"mbc": {"times": {"finding median": 0.0033922195434570312, "partitioning set": 0.027561187744140625, "shuffling constraints": 0.03943347930908203, "solving 1D LP": 0.03251838684082031, "solving 2D LP": 0.04914522171020508, "finding bridge points": 0.02995467185974121, "pruning between line points": 0.018335342407226562, "mbc": 0.23250341415405273}, "total": 0.23250341415405273, "total_profiled": 0.20034050941467285, "unaccounted": 0.03216290473937988}, "mbc2": {"times": {"extra pruning step": 0.07657957077026367, "finding median": 0.0013115406036376953, "partitioning set": 0.009799957275390625, "shuffling constraints": 0.01538705825805664, "solving 1D LP": 0.018373966217041016, "solving 2D LP": 0.023766040802001953, "finding bridge points": 0.01176595687866211, "pruning between line points": 0.0074269771575927734, "mbc2": 0.1977553367614746}, "total": 0.1977553367614746, "total_profiled": 0.16441106796264648, "unaccounted": 0.033344268798828125}, "mbc_no_shuffle": {"times": {"finding median": 0.003788471221923828, "partitioning set": 0.0294649600982666, "solving 1D LP": 0.01887059211730957, "solving 2D LP": 0.028406858444213867, "finding bridge points": 0.026410341262817383, "pruning between line points": 0.01907038688659668, "mbc_no_shuffle": 0.16133356094360352}, "total": 0.16133356094360352, "total_profiled": 0.12601161003112793, "unaccounted": 0.035321950912475586}, "mbc2_no_shuffle": {"times": {"extra pruning step": 0.07852339744567871, "finding median": 0.0014028549194335938, "partitioning set": 0.010036468505859375, "solving 1D LP": 0.008901834487915039, "solving 2D LP": 0.012744665145874023, "finding bridge points": 0.012570381164550781, "pruning between line points": 0.007673025131225586, "mbc2_no_shuffle": 0.14619135856628418}, "total": 0.14619135856628418, "total_profiled": 0.1318526268005371, "unaccounted": 0.01433873176574707}}, "60000": {"mbc": {"times": {"finding median": 0.006650209426879883, "partitioning set": 0.06662178039550781, "shuffling constraints": 0.08104324340820312, "solving 1D LP": 0.08928251266479492, "solving 2D LP": 0.12702107429504395, "finding bridge points": 0.060240983963012695, "pruning between line points": 0.042349815368652344, "mbc": 0.5576188564300537}, "total": 0.5576188564300537, "total_profiled": 0.4732096195220947, "unaccounted": 0.08440923690795898}, "mbc2": {"times": {"extra pruning step": 0.1567850112915039, "finding median": 0.001898050308227539, "partitioning set": 0.022631168365478516, "shuffling constraints": 0.031010150909423828, "solving 1D LP": 0.042766571044921875, "solving 2D LP": 0.0571141242980957, "finding bridge points": 0.025599241256713867, "pruning between line points": 0.014193058013916016, "mbc2": 0.36343836784362793}, "total": 0.36343836784362793, "total_profiled": 0.35199737548828125, "unaccounted": 0.01144099235534668}, "mbc_no_shuffle": {"times": {"finding median": 0.00703120231628418, "partitioning set": 0.06458783149719238, "solving 1D LP": 0.050481319427490234, "solving 2D LP": 0.07022786140441895, "finding bridge points": 0.06221938133239746, "pruning between line points": 0.04131031036376953, "mbc_no_shuffle": 0.3914146423339844}, "total": 0.3914146423339844, "total_profiled": 0.29585790634155273, "unaccounted": 0.09555673599243164}, "mbc2_no_shuffle": {"times": {"extra pruning step": 0.15882325172424316, "finding median": 0.0018322467803955078, "partitioning set": 0.024189233779907227, "solving 1D LP": 0.02034759521484375, "solving 2D LP": 0.02784895896911621, "finding bridge points": 0.022724390029907227, "pruning between line points": 0.015139102935791016, "mbc2_no_shuffle": 0.30243945121765137}, "total": 0.30243945121765137, "total_profiled": 0.2709047794342041, "unaccounted": 0.031534671783447266}}} + 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)) - ### PREPARE DATA ### - - num_points = list(results.keys()) - algorithms = [algorithm for algorithm in results[num_points[0]].keys()] + algorithms = list(results.keys()) steps = ( + "other", "finding median", "partitioning set", - "solving 1D LP", - "solving 2D LP", + "flipping constraints", + "solving 2D", "finding bridge points", "pruning between line points", "shuffling constraints", "extra pruning step", ) - def prep_df(df, name): - df = df.stack().reset_index() - df.columns = ['c1', 'c2', 'values'] - df['DF'] = name - return df + data = [[result["times"].get(step, 0) * 1000 for result in results.values()] + for step in steps] - dfs = [] - for step in steps: - res = [[data["times"].get(step, 0) - for algorithm, data in results[points].items()] - for points in num_points] - df = pd.DataFrame(res, index=num_points, columns=algorithms) - dfs.append(prep_df(df, step)) - - df = pd.concat(dfs) - - ### PLOT DATA ### - - chart = alt.Chart(df).mark_bar().encode( - - # tell Altair which field to group columns on - x=alt.X('c2:N', - axis=alt.Axis( - title='')), - - # tell Altair which field to use as Y values and how to calculate - y=alt.Y('sum(values):Q', - axis=alt.Axis( - grid=False, - title='')), - - # tell Altair which field to use to use as the set of columns to be represented in each group - column=alt.Column('c1:N', - axis=alt.Axis( - title='')), - - # tell Altair which field to use for color segmentation - color=alt.Color('DF:N', - scale=alt.Scale( - # make it look pretty with an enjoyable color pallet - range=['#96ceb4', '#ffcc5c', '#ff6f69'], - ), - )) \ - .configure_facet_cell( - # remove grid lines around column clusters - strokeWidth=0.0) - chart.savechart('chart.html') + 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__': diff --git a/h2/util.py b/h2/util.py index 9357ce8..7c10c26 100644 --- a/h2/util.py +++ b/h2/util.py @@ -132,3 +132,67 @@ class Side(Enum): ON = auto() ABOVE = auto() BELOW = auto() + + +def stacked_bar(data, series_labels, category_labels=None, + show_values=False, value_format="{}", y_label=None, + grid=True, reverse=False): + """ + Plots a stacked bar chart with the data and labels provided (https://stackoverflow.com/a/50205834). + + Keyword arguments: + data -- 2-dimensional numpy array or nested list + containing data for each series in rows + series_labels -- list of series labels (these appear in + the legend) + category_labels -- list of category labels (these appear + on the x-axis) + show_values -- If True then numeric value labels will + be shown on each bar + value_format -- Format string for numeric value labels + (default is "{}") + y_label -- Label for y-axis (str) + grid -- If True display grid + reverse -- If True reverse the order that the + series are displayed (left-to-right + or right-to-left) + """ + + ny = len(data[0]) + ind = list(range(ny)) + + axes = [] + cum_size = np.zeros(ny) + + data = np.array(data) + + if reverse: + data = np.flip(data, axis=1) + category_labels = reversed(category_labels) + + for i, row_data in enumerate(data): + axes.append(plt.bar(ind, row_data, bottom=cum_size, + label=series_labels[i])) + cum_size += row_data + + if category_labels: + plt.xticks(ind, category_labels) + + if y_label: + plt.ylabel(y_label) + + plt.legend() + + if grid: + plt.grid() + + if show_values: + for axis in axes: + for bar in axis: + w, h = bar.get_width(), bar.get_height() + if h != 0: + plt.text(bar.get_x() + w/2, bar.get_y() + h/2, + value_format.format(h), ha="center", + va="center") + + plt.show()