From 3461a1f7aa58d845f2140c7395df767d4d2119d7 Mon Sep 17 00:00:00 2001 From: Qinyi Chen Date: Tue, 9 Jun 2020 18:03:07 -0700 Subject: [PATCH 1/2] Bug fix. --- src/laptools/clap_new.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/laptools/clap_new.py b/src/laptools/clap_new.py index 40ed234..9d9da2b 100644 --- a/src/laptools/clap_new.py +++ b/src/laptools/clap_new.py @@ -77,7 +77,7 @@ def costs(cost_matrix): # Since there are at least as many columns as rows, row_idxs should # be identical to np.arange(n_rows). We depend on this. row_idxs = np.arange(n_rows) - row4col, col4row, u, v = lap.solve(cost_matrix) + row4col, col4row, u, v = lap._solve(cost_matrix) # Column vector of costs of each assignment in the lsap solution. lsap_costs = cost_matrix[row_idxs, col4row] From ea753435baa510196b6386433aa6af02624b66d0 Mon Sep 17 00:00:00 2001 From: Qinyi Chen Date: Tue, 9 Jun 2020 18:04:08 -0700 Subject: [PATCH 2/2] Add benchmarks for clap solvers --- benchmarks/bench_clap.pdf | Bin 0 -> 12836 bytes benchmarks/bench_clap.py | 85 ++++++++++++++++++++++++++++++++++++++ benchmarks/plot_clap.py | 78 ++++++++++++++++++++++++++++++++++ src/laptools/__init__.py | 2 +- tox.ini | 6 +-- 5 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 benchmarks/bench_clap.pdf create mode 100644 benchmarks/bench_clap.py create mode 100644 benchmarks/plot_clap.py diff --git a/benchmarks/bench_clap.pdf b/benchmarks/bench_clap.pdf new file mode 100644 index 0000000000000000000000000000000000000000..82a0fa267971ffa8bcdd8411997091bcc0e2c839 GIT binary patch literal 12836 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 XFDpnCDsB6t!SRKFkdV+e(Lww_rR1kg literal 0 HcmV?d00001 diff --git a/benchmarks/bench_clap.py b/benchmarks/bench_clap.py new file mode 100644 index 0000000..69d1f03 --- /dev/null +++ b/benchmarks/bench_clap.py @@ -0,0 +1,85 @@ +import argparse + +import numpy as np +import pyperf + + +def get_solvers(): + from laptools.clap import costs as costs_old + from laptools.clap_new import costs as costs_new + + return { + "clap_old": costs_old, + "clap_new": costs_new, + } + + +def time_func(n_inner_loops, solver, shape): + cost_matrix = np.random.random(shape) + t0 = pyperf.perf_counter() + for i in range(n_inner_loops): + solver(cost_matrix) + return pyperf.perf_counter() - t0 + + +def get_bench_name(size, solver_name): + return "{}x{}-{}".format(size[0], size[1], solver_name) + + +def parse_args(benchopts): + parser = argparse.ArgumentParser() + parser.add_argument( + "--min-row-size-pow", + type=int, + metavar="POW", + default=1, + help="Smallest number of rows is 2^POW.", + ) + parser.add_argument( + "--min-col-size-pow", + type=int, + metavar="POW", + default=1, + help="Smallest number of cols is 2^POW.", + ) + parser.add_argument( + "--max-row-size-pow", + type=int, + metavar="POW", + default=2, + help="Largest number of rows is 2^POW.", + ) + parser.add_argument( + "--max-col-size-pow", + type=int, + metavar="POW", + default=2, + help="Largest number of cols is 2^POW.", + ) + return parser.parse_args(benchopts) + + +def add_cmdline_args(cmd, args): + cmd.append("--") + cmd.extend(args.benchopts) + + +def main(): + runner = pyperf.Runner(add_cmdline_args=add_cmdline_args) + runner.argparser.add_argument("benchopts", nargs="*") + args = parse_args(runner.parse_args().benchopts) + + solvers = get_solvers() + sizes = [ + (n_rows, n_cols) + for n_rows in 2 ** np.arange(args.min_row_size_pow, args.max_row_size_pow + 1) + for n_cols in 2 ** np.arange(args.min_col_size_pow, args.max_col_size_pow + 1) + ] + for size in sizes: + for solver_name, solver_func in solvers.items(): + bench_name = get_bench_name(size, solver_name) + runner.bench_time_func(bench_name, time_func, solver_func, size) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/plot_clap.py b/benchmarks/plot_clap.py new file mode 100644 index 0000000..1b889be --- /dev/null +++ b/benchmarks/plot_clap.py @@ -0,0 +1,78 @@ +import argparse + +import matplotlib.pyplot as plt +import numpy as np +import pyperf + + +def get_solver_from_bench(bench): + """Extract the solver from a benchmark. + + Assumes each benchmark's name is of the format: + + "n_rows n_cols solver_name" + """ + _, solver = bench.get_name().split("-") + return solver + + +def get_size_from_bench(bench): + """Extract the problem size from a benchmark. + + Assumes each benchmark's name is of the format: + + "n_rows n_cols solver_name" + """ + size, _ = bench.get_name().split("-") + n_rows, n_cols = size.split("x") + return (int(n_rows), int(n_cols)) + + +def get_solver_to_benches(suite): + """Get a map from each solver to a list of its benchmarks.""" + solver_to_benches = {} + for bench in suite: + solver = get_solver_from_bench(bench) + if solver not in solver_to_benches: + solver_to_benches[solver] = [] + solver_to_benches[solver].append(bench) + return solver_to_benches + + +def get_data_from_benches(benches): + """Extract the problem size and runtimes from each benchmark.""" + sizes = [get_size_from_bench(bench) for bench in benches] + times = [bench.get_values() for bench in benches] + return np.array(sizes), np.array(times) + + +# TODO: can't do a loglog plot +def plot_suite(suite): + """Plot the performance of each solver.""" + solver_to_benches = get_solver_to_benches(suite) + for solver, benches in solver_to_benches.items(): + sizes, times = get_data_from_benches(benches) + medians = np.median(times, axis=1) + sizes_str = ["{}x{}".format(n_rows, n_cols) for n_rows, n_cols in sizes] + plt.plot(sizes_str, medians, label=solver) + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("suitefile") + parser.add_argument("outputfile") + return parser.parse_args() + + +def main(): + args = parse_args() + suite = pyperf.BenchmarkSuite.load(args.suitefile) + plot_suite(suite) + plt.legend() + plt.xlabel("Problem size") + plt.ylabel("Time to solve") + plt.savefig(args.outputfile) + + +if __name__ == "__main__": + main() diff --git a/src/laptools/__init__.py b/src/laptools/__init__.py index acfedcb..67eeefd 100644 --- a/src/laptools/__init__.py +++ b/src/laptools/__init__.py @@ -13,4 +13,4 @@ from . import clap, clap_new, lap -__all__ = ["clap", "lap"] +__all__ = ["clap", "clap_new", "lap"] diff --git a/tox.ini b/tox.ini index 8d00cbf..6ec041d 100644 --- a/tox.ini +++ b/tox.ini @@ -69,6 +69,6 @@ extras = perf commands = # https://pyperf.readthedocs.io/en/latest/runner.html#runner-cli # tox -e perf -- --values=10 --processes=100 # Run 100 trials for each method on each of 10 different matrices - # tox -e perf -- -- --min-size-pow=3 --max-size-pow=5 # Use matrices ranging in size from 2^3 to 2^5 - python bench.py -o {envtmpdir}/bench.json --values=1 --processes=1 {posargs} - python plot.py {envtmpdir}/bench.json bench.pdf + # tox -e perf -- -- --min-row-size-pow=3 --max-row-size-pow=5 --min-col-size-pow=3 --max-col-size-pow=5 # Use matrices ranging in size from 2^3 to 2^5 + python bench_clap.py -o {envtmpdir}/bench.json --values=1 --processes=1 {posargs} + python plot_clap.py {envtmpdir}/bench.json bench_clap.pdf