aboutsummaryrefslogtreecommitdiffstats
path: root/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.cpp')
-rw-r--r--Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.cpp261
1 files changed, 261 insertions, 0 deletions
diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.cpp b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.cpp
new file mode 100644
index 00000000..49994244
--- /dev/null
+++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.cpp
@@ -0,0 +1,261 @@
1
2#include <cmath>
3#include <cstdlib>
4#include <iostream>
5#include <stdexcept>
6
7#include <jni.h>
8
9#include "CbcBranchDefaultDecision.hpp"
10#include "CbcCompareDefault.hpp"
11#include "CbcHeuristic.hpp"
12#include "CbcHeuristicLocal.hpp"
13#include "CbcModel.hpp"
14#include "CglClique.hpp"
15#include "CglFlowCover.hpp"
16#include "CglGomory.hpp"
17#include "CglKnapsackCover.hpp"
18#include "CglMixedIntegerRounding.hpp"
19#include "CglOddHole.hpp"
20#include "CglProbing.hpp"
21#include "CoinModel.hpp"
22#include "OsiClpSolverInterface.hpp"
23
24#include "viatracbc.hpp"
25
26static const char *const kCbcExceptionClassName = "hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcException";
27static const char *const kRuntimeExceptionClassName = "java/lang/RuntimeException";
28
29static const jint kCbcSolutionBounded = 0;
30static const jint kCbcSolutionUnbounded = 1;
31static const jint kCbcUnsat = 2;
32static const jint kCbcAbandoned = 3;
33static const jint kCbcTimeout = 4;
34static const jint kCbcError = 5;
35
36static CoinModel CreateModel(JNIEnv *env, jdoubleArray columnLowerBoundsArray,
37 jdoubleArray columnUpperBoundsArray, jintArray rowStartsArray, jintArray columnIndicesArray,
38 jdoubleArray entriedArray, jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray,
39 jdoubleArray objectiveArray);
40static void CreateModelColumns(JNIEnv *env, jdoubleArray columnLowerBoundsArray,
41 jdoubleArray columnUpperBoundsArray, jdoubleArray objectiveArray, CoinModel &build);
42static void CreateModelRows(JNIEnv *env, jintArray rowStartsArray, jintArray columnIndicesArray,
43 jdoubleArray entriesArray, jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray,
44 CoinModel &build);
45static jint SolveModel(CoinModel &build, jdouble timeoutSeconds, jboolean silent, jdouble &value);
46static void ThrowException(JNIEnv *env, const char *message);
47
48template <
49 typename Array,
50 typename Element,
51 Element *(JNIEnv::*GetElementsPtr)(Array, jboolean *),
52 void (JNIEnv::*ReleaseElementsPtr)(Array, Element *, jint)
53>
54class PinnedArray {
55public:
56 PinnedArray(JNIEnv *env, Array array)
57 : env_{env}, array_{array}, elements_{(env->*GetElementsPtr)(array, nullptr)} {
58 if (elements_ == nullptr) {
59 throw std::runtime_error("Failed to pin array elements");
60 }
61 }
62 PinnedArray(const PinnedArray &) = delete;
63 PinnedArray(PinnedArray &&) = delete;
64 PinnedArray &operator=(const PinnedArray &) = delete;
65 PinnedArray &operator=(PinnedArray &&) = delete;
66 ~PinnedArray() {
67 (env_->*ReleaseElementsPtr)(array_, elements_, 0);
68 }
69
70 operator Element *() { return elements_; }
71 operator const Element *() const { return elements_; }
72
73private:
74 JNIEnv *env_;
75 Array array_;
76 Element *elements_;
77};
78
79using PinnedIntArray = PinnedArray<jintArray, jint, &JNIEnv::GetIntArrayElements, &JNIEnv::ReleaseIntArrayElements>;
80using PinnedDoubleArray = PinnedArray<jdoubleArray, jdouble, &JNIEnv::GetDoubleArrayElements, &JNIEnv::ReleaseDoubleArrayElements>;
81
82jint Java_hu_bme_mit_inf_dslreasoner_ilp_cbc_CbcSolver_solveIlpProblem(
83 JNIEnv *env, jclass klazz, jdoubleArray columnLowerBoundsArray, jdoubleArray columnUpperBoundsArray,
84 jintArray rowStartsArray, jintArray columnIndicesArray, jdoubleArray entriesArray,
85 jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray, jdoubleArray objectiveArray,
86 jdoubleArray outputArray, jdouble timeoutSeconds, jboolean silent) {
87 try {
88 auto build = CreateModel(env, columnLowerBoundsArray, columnUpperBoundsArray,
89 rowStartsArray, columnIndicesArray, entriesArray, rowLowerBoundsArray, rowUpperBoundsArray,
90 objectiveArray);
91 double value;
92 jint result = SolveModel(build, timeoutSeconds, silent, value);
93 if (result == kCbcSolutionBounded) {
94 PinnedDoubleArray output{env, outputArray};
95 *output = value;
96 }
97 return result;
98 } catch (const std::exception &e) {
99 ThrowException(env, e.what());
100 } catch (...) {
101 ThrowException(env, "Unknown solver error");
102 }
103 return kCbcError;
104}
105
106CoinModel CreateModel(JNIEnv *env, jdoubleArray columnLowerBoundsArray,
107 jdoubleArray columnUpperBoundsArray, jintArray rowStartsArray, jintArray columnIndicesArray,
108 jdoubleArray entriesArray, jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray,
109 jdoubleArray objectiveArray) {
110 CoinModel build;
111 CreateModelColumns(env, columnLowerBoundsArray, columnUpperBoundsArray, objectiveArray, build);
112 CreateModelRows(env, rowStartsArray, columnIndicesArray, entriesArray, rowLowerBoundsArray,
113 rowUpperBoundsArray, build);
114 return build;
115}
116
117void CreateModelColumns(JNIEnv *env, jdoubleArray columnLowerBoundsArray,
118 jdoubleArray columnUpperBoundsArray, jdoubleArray objectiveArray, CoinModel &build) {
119 int numColumns = env->GetArrayLength(columnLowerBoundsArray);
120 PinnedDoubleArray columnLowerBounds{env, columnLowerBoundsArray};
121 PinnedDoubleArray columnUpperBounds{env, columnUpperBoundsArray};
122 PinnedDoubleArray objective{env, objectiveArray};
123 for (int i = 0; i < numColumns; i++) {
124 build.setColumnBounds(i, columnLowerBounds[i], columnUpperBounds[i]);
125 build.setObjective(i, objective[i]);
126 build.setInteger(i);
127 }
128}
129
130void CreateModelRows(JNIEnv *env, jintArray rowStartsArray, jintArray columnIndicesArray,
131 jdoubleArray entriesArray, jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray,
132 CoinModel &build) {
133 int numRows = env->GetArrayLength(rowLowerBoundsArray);
134 PinnedIntArray rowStarts{env, rowStartsArray};
135 PinnedIntArray columnIndices{env, columnIndicesArray};
136 PinnedDoubleArray entries{env, entriesArray};
137 PinnedDoubleArray rowLowerBounds{env, rowLowerBoundsArray};
138 PinnedDoubleArray rowUpperBounds{env, rowUpperBoundsArray};
139 for (int i = 0; i < numRows; i++) {
140 int rowStart = rowStarts[i];
141 int numbersInRow = rowStarts[i + 1] - rowStart;
142 build.addRow(numbersInRow, &columnIndices[rowStart], &entries[rowStart],
143 rowLowerBounds[i], rowUpperBounds[i]);
144 }
145}
146
147jint SolveModel(CoinModel &build, jdouble timeoutSeconds, jboolean silent, jdouble &value) {
148 OsiClpSolverInterface solver;
149 solver.loadFromCoinModel(build);
150 CbcModel model{solver};
151
152 model.setDblParam(CbcModel::CbcMaximumSeconds, timeoutSeconds);
153 if (silent == JNI_FALSE) {
154 model.messageHandler()->setLogLevel(2);
155 model.solver()->messageHandler()->setLogLevel(1);
156 } else {
157 model.solver()->setHintParam(OsiDoReducePrint, true, OsiHintTry);
158 model.messageHandler()->setLogLevel(0);
159 model.solver()->messageHandler()->setLogLevel(0);
160 }
161
162 // Cut generators and heuristics are used according to
163 // https://github.com/coin-or/Cbc/blob/6b977b6707f1755520c64fea57b95891c1f3ddc0/Cbc/examples/sample2.cpp
164
165 CglProbing probing;
166 probing.setUsingObjective(true);
167 probing.setMaxPass(1);
168 probing.setMaxPassRoot(5);
169 probing.setMaxProbe(10);
170 probing.setMaxProbeRoot(1000);
171 probing.setMaxLook(50);
172 probing.setMaxLookRoot(500);
173 probing.setMaxElements(200);
174 probing.setRowCuts(3);
175 model.addCutGenerator(&probing, -1, "Probing");
176
177 CglGomory gomory;
178 gomory.setLimit(300);
179 model.addCutGenerator(&gomory, -1, "Gomory");
180
181 CglKnapsackCover knapsackCover;
182 model.addCutGenerator(&knapsackCover, -1, "KnapsackCover");
183
184 CglClique clique;
185 clique.setStarCliqueReport(false);
186 clique.setRowCliqueReport(false);
187 model.addCutGenerator(&clique, -1, "Clique");
188
189 CglFlowCover flowCover;
190 model.addCutGenerator(&flowCover, -1, "FlowCover");
191
192 CglMixedIntegerRounding mixedIntegerRounding;
193 model.addCutGenerator(&mixedIntegerRounding, -1, "MixedIntegerRounding");
194
195 OsiClpSolverInterface *osiClp = dynamic_cast<OsiClpSolverInterface *>(model.solver());
196 if (osiClp != nullptr) {
197 osiClp->setSpecialOptions(128);
198 osiClp->setupForRepeatedUse(0, 0);
199 }
200
201 CbcRounding rounding;
202 model.addHeuristic(&rounding);
203
204 CbcHeuristicLocal localHeuristic;
205 model.addHeuristic(&localHeuristic);
206
207 CbcBranchDefaultDecision branchDecision;
208 model.setBranchingMethod(&branchDecision);
209
210 CbcCompareDefault nodeComparison;
211 model.setNodeComparison(nodeComparison);
212
213 model.initialSolve();
214
215 if (model.isInitialSolveProvenPrimalInfeasible()) {
216 return kCbcUnsat;
217 }
218 if (model.isInitialSolveAbandoned()) {
219 return kCbcTimeout;
220 }
221
222 model.setMinimumDrop(CoinMin(1.0, fabs(model.getMinimizationObjValue()) * 1.0e-3 + 1.0e-4));
223 model.setMaximumCutPassesAtRoot(-100);
224 model.setNumberStrong(10);
225 model.solver()->setIntParam(OsiMaxNumIterationHotStart, 100);
226
227 model.branchAndBound();
228
229 if (model.isProvenInfeasible()) {
230 return kCbcUnsat;
231 }
232 if (model.isProvenDualInfeasible()) {
233 return kCbcSolutionUnbounded;
234 }
235 if (model.isProvenOptimal()) {
236 value = model.getMinimizationObjValue();
237 return kCbcSolutionBounded;
238 }
239 if (model.maximumSecondsReached()) {
240 return kCbcTimeout;
241 }
242 return kCbcAbandoned;
243}
244
245void ThrowException(JNIEnv *env, const char *message) {
246 jclass exceptionClass = env->FindClass(kCbcExceptionClassName);
247 if (exceptionClass == nullptr) {
248 std::cerr << "WARNING: " << kCbcExceptionClassName << " class was not found" << std::endl;
249 exceptionClass = env->FindClass(kRuntimeExceptionClassName);
250 if (exceptionClass == nullptr) {
251 std::cerr << "FATAL: " << kRuntimeExceptionClassName << " class was not found" << std::endl;
252 std::cerr << "FATAL: " << message << std::endl;
253 std::exit(EXIT_FAILURE);
254 }
255 }
256 if (env->ThrowNew(exceptionClass, message) < 0) {
257 std::cerr << "FATAL: Could not throw java exception" << std::endl;
258 std::cerr << "FATAL: " << message << std::endl;
259 std::exit(EXIT_FAILURE);
260 }
261}