From 7b44cd79e7a18e675784ff6eafb2a4013e5e37ab Mon Sep 17 00:00:00 2001 From: Alinson S Xavier Date: Sat, 1 Feb 2020 08:21:47 -0600 Subject: [PATCH] Add benchmark scripts --- benchmark/Makefile | 29 +++++++ benchmark/benchmark.py | 177 +++++++++++++++++++++++++++++++++++++++++ docs/figures/mwss.png | Bin 33237 -> 0 bytes docs/problems.md | 8 +- 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 benchmark/Makefile create mode 100755 benchmark/benchmark.py delete mode 100644 docs/figures/mwss.png diff --git a/benchmark/Makefile b/benchmark/Makefile new file mode 100644 index 0000000..b170313 --- /dev/null +++ b/benchmark/Makefile @@ -0,0 +1,29 @@ +CHALLENGES := \ + stab/ChallengeA \ + knapsack/ChallengeA + +main: $(addsuffix /performance.png, $(CHALLENGES)) + +%/training_data.bin: + python benchmark.py train $* + +%/benchmark_baseline.csv: %/training_data.bin + python benchmark.py test-baseline $* + +%/benchmark_ml.csv: %/benchmark_baseline.csv + python benchmark.py test-ml $* + +%/performance.png: %/benchmark_ml.csv + python benchmark.py charts $* + +clean: + rm -rvf stab knapsack + +clean-csv: + rm -rvf */*/*.csv + +clean-png: + rm -rfv */*/*.png + +.PHONY: clean +.SECONDARY: diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py new file mode 100755 index 0000000..8941705 --- /dev/null +++ b/benchmark/benchmark.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +"""Benchmark script + +Usage: + benchmark.py train + benchmark.py test-baseline + benchmark.py test-ml + benchmark.py charts + +Options: + -h --help Show this screen +""" +from docopt import docopt +import importlib, pathlib +from miplearn import LearningSolver, BenchmarkRunner +from miplearn.warmstart import WarmStartComponent +from miplearn.branching import BranchPriorityComponent +from numpy import median +import pyomo.environ as pe +import pickle + +args = docopt(__doc__) +basepath = args[""] +pathlib.Path(basepath).mkdir(parents=True, exist_ok=True) + + +def save(obj, filename): + print("Writing %s..." % filename) + with open(filename, "wb") as file: + pickle.dump(obj, file) + + +def load(filename): + import pickle + with open(filename, "rb") as file: + return pickle.load(file) + + +def train_solver_factory(): + solver = pe.SolverFactory('gurobi_persistent') + solver.options["threads"] = 4 + solver.options["TimeLimit"] = 300 + return solver + + +def test_solver_factory(): + solver = pe.SolverFactory('gurobi_persistent') + solver.options["threads"] = 4 + solver.options["TimeLimit"] = 300 + return solver + + +def train(): + problem_name, challenge_name = args[""].split("/") + pkg = importlib.import_module("miplearn.problems.%s" % problem_name) + challenge = getattr(pkg, challenge_name)() + train_instances = challenge.training_instances + test_instances = challenge.test_instances + solver = LearningSolver( + internal_solver_factory=train_solver_factory, + components={ + "warm-start": WarmStartComponent(), + "branch-priority": BranchPriorityComponent(), + }, + ) + solver.parallel_solve(train_instances, n_jobs=10) + solver.save_state("%s/training_data.bin" % basepath) + save(train_instances, "%s/train_instances.bin" % basepath) + save(test_instances, "%s/test_instances.bin" % basepath) + + +def test_baseline(): + solvers = { + "baseline": LearningSolver( + internal_solver_factory=test_solver_factory, + components={}, + ), + } + test_instances = load("%s/test_instances.bin" % basepath) + benchmark = BenchmarkRunner(solvers) + benchmark.parallel_solve(test_instances, n_jobs=10) + benchmark.save_results("%s/benchmark_baseline.csv" % basepath) + + +def test_ml(): + solvers = { + "ml-exact": LearningSolver( + internal_solver_factory=test_solver_factory, + components={ + "warm-start": WarmStartComponent(), + "branch-priority": BranchPriorityComponent(), + }, + ), + "ml-heuristic": LearningSolver( + internal_solver_factory=test_solver_factory, + mode="heuristic", + components={ + "warm-start": WarmStartComponent(), + "branch-priority": BranchPriorityComponent(), + }, + ), + } + test_instances = load("%s/test_instances.bin" % basepath) + benchmark = BenchmarkRunner(solvers) + benchmark.load_state("%s/training_data.bin" % basepath) + benchmark.fit() + benchmark.load_results("%s/benchmark_baseline.csv" % basepath) + benchmark.parallel_solve(test_instances, n_jobs=10) + benchmark.save_results("%s/benchmark_ml.csv" % basepath) + + +def charts(): + import matplotlib.pyplot as plt + import seaborn as sns + sns.set_style("whitegrid") + sns.set_palette("Blues_r") + benchmark = BenchmarkRunner({}) + benchmark.load_results("%s/benchmark_ml.csv" % basepath) + results = benchmark.raw_results() + results["Gap (%)"] = results["Gap"] * 100.0 + palette={ + "baseline": "#9b59b6", + "ml-exact": "#3498db", + "ml-heuristic": "#95a5a6" + } + fig, axes = plt.subplots(nrows=1, + ncols=3, + figsize=(10,4), + gridspec_kw={'width_ratios': [3, 3, 2]}, + ) + sns.stripplot(x="Solver", + y="Wallclock Time", + data=results, + ax=axes[0], + jitter=0.25, + palette=palette, + ); + sns.barplot(x="Solver", + y="Wallclock Time", + data=results, + ax=axes[0], + errwidth=0., + alpha=0.3, + palette=palette, + estimator=median, + ); + axes[0].set(ylabel='Wallclock Time (s)') + axes[1].set_ylim(-0.5, 5.5) + sns.stripplot(x="Solver", + y="Gap (%)", + jitter=0.25, + data=results[results["Solver"] != "ml-heuristic"], + ax=axes[1], + palette=palette, + ); + axes[2].set_ylim(0.95,1.01) + sns.stripplot(x="Solver", + y="Relative Lower Bound", + jitter=0.25, + data=results[results["Solver"] == "ml-heuristic"], + ax=axes[2], + palette=palette, + ); + fig.tight_layout() + plt.savefig("%s/performance.png" % basepath, + bbox_inches='tight', + dpi=150) + +if __name__ == "__main__": + if args["train"]: + train() + if args["test-baseline"]: + test_baseline() + if args["test-ml"]: + test_ml() + if args["charts"]: + charts() \ No newline at end of file diff --git a/docs/figures/mwss.png b/docs/figures/mwss.png deleted file mode 100644 index 76735096fc6d9b41266f506ae138661b3a8b358c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33237 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

w(VE_OC diff --git a/docs/problems.md b/docs/problems.md index e085382..bcef479 100644 --- a/docs/problems.md +++ b/docs/problems.md @@ -49,7 +49,7 @@ MaxWeightStableSetGenerator(w=uniform(loc=100., scale=50.), #### Challenge A -![alt](figures/mwss.png) +![alt](figures/benchmark_stab_a.png) ## Multidimensional 0-1 Knapsack Problem @@ -95,6 +95,12 @@ If a probability distribution `w_jitter` is provided, then item weights will be By default, all generated prices, weights and capacities are rounded to the nearest integer number. If `round=False` is provided, this rounding will be disabled. +### Benchmark results + +#### Challenge A + +![alt](figures/benchmark_knapsack_a.png) + !!! note "References" * Freville, Arnaud, and Gérard Plateau. *An efficient preprocessing procedure for the multidimensional 0–1 knapsack problem.* Discrete applied mathematics 49.1-3 (1994): 189-212. * Fréville, Arnaud. *The multidimensional 0–1 knapsack problem: An overview.* European Journal of Operational Research 155.1 (2004): 1-21. \ No newline at end of file