From cdfb1ad0539eec5605070793bcb7a976bf04f983 Mon Sep 17 00:00:00 2001 From: Alinson S Xavier Date: Wed, 29 Jan 2020 09:56:45 -0600 Subject: [PATCH] README.md: Add benchmark results --- README.md | 38 +++++++++++++++++++++++++++++++++----- figures/mwss.png | Bin 0 -> 20549 bytes 2 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 figures/mwss.png diff --git a/README.md b/README.md index 3a8922d..b2b6bf0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ MIPLearn ======== -**MIPLearn** is a flexible and extensible framework for *Learning-Enhanced Mixed-Integer Optimization*, an approach aimed at efficiently handling challenging discrete optimization problems that need to be repeatedly solved with only relatively minor changes to the input data. The package uses Machine Learning (ML) to automatically identify patterns in previously solved instances of the problem, or in the solution process itself, and produces hints that can guide a traditional MIP solver towards the optimal solution faster. For particular classes of problems, this approach has been shown to provide significant performance benefits (see references below). +**MIPLearn** is a flexible and extensible framework for *Learning-Enhanced Mixed-Integer Optimization*, an approach aimed at efficiently handling challenging discrete optimization problems that need to be repeatedly solved with only relatively minor changes to the input data. The package uses Machine Learning (ML) to automatically identify patterns in previously solved instances of the problem, or in the solution process itself, and produces hints that can guide a traditional MIP solver towards the optimal solution faster. For particular classes of problems, this approach has been shown to provide significant performance benefits (see [benchmark results](#benchmark-results) and [references](#references) below). Table of Contents ----------------- @@ -13,16 +13,19 @@ Table of Contents * [Obtaining heuristic solutions](#obtaining-heuristic-solutions) * [Saving and loading solver state](#saving-and-loading-solver-state) * [Solving training instances in parallel](#solving-training-instances-in-parallel) - * [Benchmarking](#benchmarking) + * [Benchmarks](#benchmarks) * [Using BenchmarkRunner](#using-benchmarkrunner) * [Saving and loading benchmark results](#saving-and-loading-benchmark-results) + * [Benchmark problems](#benchmark-problems) + * [Benchmark results](#benchmark-results) + * [Maximum Weight Stable Set Problem](#maximum-weight-stable-set-problem) * [Customization](#customization) * [Selecting the internal MIP solver](#selecting-the-internal-mip-solver) * [Current Limitations](#current-limitations) - * [References](#references) * [Authors](#authors) * [Acknowledgements](#acknowledgements) * [License](#license) + * [References](#references) Features -------- @@ -132,8 +135,8 @@ solver.solve(test_instance) After all training instances have been solved in parallel, the ML models can be trained and saved to disk as usual, using `fit` and `save_state`, as explained in the previous subsections. -Benchmarking ------------- +Benchmarks +---------- ### Using `BenchmarkRunner` @@ -195,6 +198,31 @@ benchmark.load_results("baseline_results.csv") benchmark.parallel_solve(test_instances) ``` +### Benchmark problems + +MIPLearn provides a selection of random instance generators for some fundamental discrete optimization problems, as well a baseline MIP and ML formulation for these problems. The included problems are the following: + +* **Maximum Weight Stable Set Problem:** Given a graph G=(V,E) with vertex weights, the problem is to find the maximum weight *stable set* of the graph, where a *stable set* a set of vertices, no two of which are adjacent. The class `MaxWeightStableSetGenerator` can generate random instances of this problem with specified probability distributions for number of vertices, edge probability and weights. + +### Benchmark results + +To illustrate the performance benefits of MIPLearn, we present a small number of computational results for some of the included benchmark problems. For more detailed computational studies, see the [references](#references) below. We compare three solvers: + +* **baseline:** Gurobi 9.0 with default settings (a conventional state-of-the-art MIP solver) +* **ml-exact:** `LearningSolver` with default settings, using Gurobi 9.0 as internal MIP solver +* **ml-heuristic:** Same as above, but with `mode="heuristic"` + +The experiments were performed on a Linux server (Ubuntu Linux 18.04 LTS) with Intel Xeon Gold 6230s (2 processors, 40 cores, 80 threads) and 256 GB RAM (DDR4, 2933 MHz). All solvers were restricted to use 4 threads, wit no time limits, and 10 instances were solved simultaneously at a time. + +#### Maximum Weight Stable Set Problem + +* Fixed random graph (200 nodes, 5% edge probability) +* Random vertex weights ~ U(100, 150) +* 300 training instances, 50 test instances + +![alt](figures/mwss.png) + + Customization ------------- diff --git a/figures/mwss.png b/figures/mwss.png new file mode 100644 index 0000000000000000000000000000000000000000..131037049bb0a63a8beea3f961955d673530fdc4 GIT binary patch literal 20549 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

U8PC?4)vm^Frm#`Roi-u<`KDgBS3B0jF7O(t&E? zL`APv1#3sh^wLo-Nn7=OtOowDSy}*Q6xV__;V_ck2q8P>wO%PJ$7V>L467d$EzXom z8NdB&-Q!1(6!rC^a_q*EOp@~(BwCp$6!}!xA^AW10*sqdq8znDB%Q3mb`?mu-ymId z`PJ_8{(A)lRZ+Kr%xLer6vq21Ol&-fS0yPaIr^eC00sdxA57GmUC^C_U1MUk{$v$2CY?xCRdpZ4?(FRBbg{)K zKb;!v@wBTSwvx;8`N?*dda2{ezWlmLP;QOA-mjz-?sb}*s(&gk8Ha!4($Z8Mr$<$> zx@RW_^{fTZR<5{r9{h~`GZ7=TK*hMg%p66*I(^h`G_P}f^z-YJK$nj_Jbbv@Y#Oy$ zVV*_Hhd5Wiprd55P^naZar+clJSrD2?t`QsV3tFz5+8T3uEwZ5!WQ{D7!%U4(@Q*@ z`~;9Dr*mBVXU|i-2!^`v^oqd0T&+#a-`xGo#UcP*JF0;)x;JQ%D>JYuIxA!3X^YHUG*2O6cqPMi&?Dnvs z@k>bLtId_Ze_!`uINZ#%3G&fz6bpYYSq~pxbsnhmSXFEGV3@CwDeRc<6DI3oqVw+N zvMaB(K8dn#dcggmv{VMAvF7t@J)5!oX&zqQ@_hN@$8Yavl1-F>s4SDahyR|vqBxyM z01AqRUlTJA=14jETyrQM1OcC#{U8jXRZdu*Eigg?O;CtvA zHUYyY?T%v-#qZx67P`{aJJ-Me@N|#O{LBq1t-QAeB@SRL3c%0D*Vh=!iWX_tZZCrx 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