From b217dfc7e7bd7beb73c8cc23ad82383309ceb697 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 18 Jul 2019 15:21:56 +0200 Subject: Implement Coin-OR CBC polyhedron saturation operator --- .../hu.bme.mit.inf.dslreasoner.ilp.cbc/.classpath | 15 ++ .../hu.bme.mit.inf.dslreasoner.ilp.cbc/.gitignore | 2 + .../hu.bme.mit.inf.dslreasoner.ilp.cbc/.project | 28 +++ .../META-INF/MANIFEST.MF | 10 + .../build.properties | 4 + .../cpp/CMakeLists.txt | 23 ++ .../cpp/viatracbc.cpp | 261 +++++++++++++++++++++ .../cpp/viatracbc.hpp | 16 ++ .../lib/libviatracbc.so | Bin 0 -> 38248 bytes .../mit/inf/dslreasoner/ilp/cbc/CbcException.java | 30 +++ .../bme/mit/inf/dslreasoner/ilp/cbc/CbcResult.java | 54 +++++ .../bme/mit/inf/dslreasoner/ilp/cbc/CbcSolver.java | 71 ++++++ 12 files changed, 514 insertions(+) create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.classpath create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.gitignore create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.project create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/META-INF/MANIFEST.MF create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/build.properties create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/CMakeLists.txt create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.cpp create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.hpp create mode 100755 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/lib/libviatracbc.so create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcException.java create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcResult.java create mode 100644 Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcSolver.java (limited to 'Solvers/ILP-Solver') diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.classpath b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.classpath new file mode 100644 index 00000000..e19039ae --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.classpath @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.gitignore b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.gitignore new file mode 100644 index 00000000..0cc6a59e --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/cpp/build/ diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.project b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.project new file mode 100644 index 00000000..6c32e464 --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/.project @@ -0,0 +1,28 @@ + + + hu.bme.mit.inf.dslreasoner.ilp.cbc + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/META-INF/MANIFEST.MF b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/META-INF/MANIFEST.MF new file mode 100644 index 00000000..04478746 --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/META-INF/MANIFEST.MF @@ -0,0 +1,10 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Cbc +Bundle-SymbolicName: hu.bme.mit.inf.dslreasoner.ilp.cbc +Bundle-Version: 1.0.0.qualifier +Automatic-Module-Name: hu.bme.mit.inf.dslreasoner.ilp.cbc +Export-Package: hu.bme.mit.inf.dslreasoner.ilp.cbc +Bundle-NativeCode: libviatracbc.so; + osname=Linux; + processor=x86_64 diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/build.properties b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/build.properties new file mode 100644 index 00000000..34d2e4d2 --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/CMakeLists.txt b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/CMakeLists.txt new file mode 100644 index 00000000..5dbcb071 --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.14.5) +project(hu.bme.mit.inf.dslreasoner.ilp.cbc) + +set(CMAKE_CXX_STANDARD 17) + +find_package(JNI REQUIRED) +find_package(PkgConfig REQUIRED) + +pkg_check_modules(CBC REQUIRED cbc) + +add_library(viatracbc SHARED viatracbc.cpp) + +target_link_libraries(viatracbc + ${JAVA_JVM_LIBRARY} + ${CBC_LIBRARIES}) +target_include_directories(viatracbc + PUBLIC ${JNI_INCLUDE_DIRS} + PRIVATE ${CBC_INCLUDE_DIRS}) + +set(VIATRACBC_NATIVES_DIR ${CMAKE_SOURCE_DIR}/../lib) +add_custom_command(TARGET viatracbc POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${VIATRACBC_NATIVES_DIR} + COMMAND ${CMAKE_COMMAND} -E copy $ ${VIATRACBC_NATIVES_DIR}) 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 @@ + +#include +#include +#include +#include + +#include + +#include "CbcBranchDefaultDecision.hpp" +#include "CbcCompareDefault.hpp" +#include "CbcHeuristic.hpp" +#include "CbcHeuristicLocal.hpp" +#include "CbcModel.hpp" +#include "CglClique.hpp" +#include "CglFlowCover.hpp" +#include "CglGomory.hpp" +#include "CglKnapsackCover.hpp" +#include "CglMixedIntegerRounding.hpp" +#include "CglOddHole.hpp" +#include "CglProbing.hpp" +#include "CoinModel.hpp" +#include "OsiClpSolverInterface.hpp" + +#include "viatracbc.hpp" + +static const char *const kCbcExceptionClassName = "hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcException"; +static const char *const kRuntimeExceptionClassName = "java/lang/RuntimeException"; + +static const jint kCbcSolutionBounded = 0; +static const jint kCbcSolutionUnbounded = 1; +static const jint kCbcUnsat = 2; +static const jint kCbcAbandoned = 3; +static const jint kCbcTimeout = 4; +static const jint kCbcError = 5; + +static CoinModel CreateModel(JNIEnv *env, jdoubleArray columnLowerBoundsArray, + jdoubleArray columnUpperBoundsArray, jintArray rowStartsArray, jintArray columnIndicesArray, + jdoubleArray entriedArray, jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray, + jdoubleArray objectiveArray); +static void CreateModelColumns(JNIEnv *env, jdoubleArray columnLowerBoundsArray, + jdoubleArray columnUpperBoundsArray, jdoubleArray objectiveArray, CoinModel &build); +static void CreateModelRows(JNIEnv *env, jintArray rowStartsArray, jintArray columnIndicesArray, + jdoubleArray entriesArray, jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray, + CoinModel &build); +static jint SolveModel(CoinModel &build, jdouble timeoutSeconds, jboolean silent, jdouble &value); +static void ThrowException(JNIEnv *env, const char *message); + +template < + typename Array, + typename Element, + Element *(JNIEnv::*GetElementsPtr)(Array, jboolean *), + void (JNIEnv::*ReleaseElementsPtr)(Array, Element *, jint) +> +class PinnedArray { +public: + PinnedArray(JNIEnv *env, Array array) + : env_{env}, array_{array}, elements_{(env->*GetElementsPtr)(array, nullptr)} { + if (elements_ == nullptr) { + throw std::runtime_error("Failed to pin array elements"); + } + } + PinnedArray(const PinnedArray &) = delete; + PinnedArray(PinnedArray &&) = delete; + PinnedArray &operator=(const PinnedArray &) = delete; + PinnedArray &operator=(PinnedArray &&) = delete; + ~PinnedArray() { + (env_->*ReleaseElementsPtr)(array_, elements_, 0); + } + + operator Element *() { return elements_; } + operator const Element *() const { return elements_; } + +private: + JNIEnv *env_; + Array array_; + Element *elements_; +}; + +using PinnedIntArray = PinnedArray; +using PinnedDoubleArray = PinnedArray; + +jint Java_hu_bme_mit_inf_dslreasoner_ilp_cbc_CbcSolver_solveIlpProblem( + JNIEnv *env, jclass klazz, jdoubleArray columnLowerBoundsArray, jdoubleArray columnUpperBoundsArray, + jintArray rowStartsArray, jintArray columnIndicesArray, jdoubleArray entriesArray, + jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray, jdoubleArray objectiveArray, + jdoubleArray outputArray, jdouble timeoutSeconds, jboolean silent) { + try { + auto build = CreateModel(env, columnLowerBoundsArray, columnUpperBoundsArray, + rowStartsArray, columnIndicesArray, entriesArray, rowLowerBoundsArray, rowUpperBoundsArray, + objectiveArray); + double value; + jint result = SolveModel(build, timeoutSeconds, silent, value); + if (result == kCbcSolutionBounded) { + PinnedDoubleArray output{env, outputArray}; + *output = value; + } + return result; + } catch (const std::exception &e) { + ThrowException(env, e.what()); + } catch (...) { + ThrowException(env, "Unknown solver error"); + } + return kCbcError; +} + +CoinModel CreateModel(JNIEnv *env, jdoubleArray columnLowerBoundsArray, + jdoubleArray columnUpperBoundsArray, jintArray rowStartsArray, jintArray columnIndicesArray, + jdoubleArray entriesArray, jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray, + jdoubleArray objectiveArray) { + CoinModel build; + CreateModelColumns(env, columnLowerBoundsArray, columnUpperBoundsArray, objectiveArray, build); + CreateModelRows(env, rowStartsArray, columnIndicesArray, entriesArray, rowLowerBoundsArray, + rowUpperBoundsArray, build); + return build; +} + +void CreateModelColumns(JNIEnv *env, jdoubleArray columnLowerBoundsArray, + jdoubleArray columnUpperBoundsArray, jdoubleArray objectiveArray, CoinModel &build) { + int numColumns = env->GetArrayLength(columnLowerBoundsArray); + PinnedDoubleArray columnLowerBounds{env, columnLowerBoundsArray}; + PinnedDoubleArray columnUpperBounds{env, columnUpperBoundsArray}; + PinnedDoubleArray objective{env, objectiveArray}; + for (int i = 0; i < numColumns; i++) { + build.setColumnBounds(i, columnLowerBounds[i], columnUpperBounds[i]); + build.setObjective(i, objective[i]); + build.setInteger(i); + } +} + +void CreateModelRows(JNIEnv *env, jintArray rowStartsArray, jintArray columnIndicesArray, + jdoubleArray entriesArray, jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray, + CoinModel &build) { + int numRows = env->GetArrayLength(rowLowerBoundsArray); + PinnedIntArray rowStarts{env, rowStartsArray}; + PinnedIntArray columnIndices{env, columnIndicesArray}; + PinnedDoubleArray entries{env, entriesArray}; + PinnedDoubleArray rowLowerBounds{env, rowLowerBoundsArray}; + PinnedDoubleArray rowUpperBounds{env, rowUpperBoundsArray}; + for (int i = 0; i < numRows; i++) { + int rowStart = rowStarts[i]; + int numbersInRow = rowStarts[i + 1] - rowStart; + build.addRow(numbersInRow, &columnIndices[rowStart], &entries[rowStart], + rowLowerBounds[i], rowUpperBounds[i]); + } +} + +jint SolveModel(CoinModel &build, jdouble timeoutSeconds, jboolean silent, jdouble &value) { + OsiClpSolverInterface solver; + solver.loadFromCoinModel(build); + CbcModel model{solver}; + + model.setDblParam(CbcModel::CbcMaximumSeconds, timeoutSeconds); + if (silent == JNI_FALSE) { + model.messageHandler()->setLogLevel(2); + model.solver()->messageHandler()->setLogLevel(1); + } else { + model.solver()->setHintParam(OsiDoReducePrint, true, OsiHintTry); + model.messageHandler()->setLogLevel(0); + model.solver()->messageHandler()->setLogLevel(0); + } + + // Cut generators and heuristics are used according to + // https://github.com/coin-or/Cbc/blob/6b977b6707f1755520c64fea57b95891c1f3ddc0/Cbc/examples/sample2.cpp + + CglProbing probing; + probing.setUsingObjective(true); + probing.setMaxPass(1); + probing.setMaxPassRoot(5); + probing.setMaxProbe(10); + probing.setMaxProbeRoot(1000); + probing.setMaxLook(50); + probing.setMaxLookRoot(500); + probing.setMaxElements(200); + probing.setRowCuts(3); + model.addCutGenerator(&probing, -1, "Probing"); + + CglGomory gomory; + gomory.setLimit(300); + model.addCutGenerator(&gomory, -1, "Gomory"); + + CglKnapsackCover knapsackCover; + model.addCutGenerator(&knapsackCover, -1, "KnapsackCover"); + + CglClique clique; + clique.setStarCliqueReport(false); + clique.setRowCliqueReport(false); + model.addCutGenerator(&clique, -1, "Clique"); + + CglFlowCover flowCover; + model.addCutGenerator(&flowCover, -1, "FlowCover"); + + CglMixedIntegerRounding mixedIntegerRounding; + model.addCutGenerator(&mixedIntegerRounding, -1, "MixedIntegerRounding"); + + OsiClpSolverInterface *osiClp = dynamic_cast(model.solver()); + if (osiClp != nullptr) { + osiClp->setSpecialOptions(128); + osiClp->setupForRepeatedUse(0, 0); + } + + CbcRounding rounding; + model.addHeuristic(&rounding); + + CbcHeuristicLocal localHeuristic; + model.addHeuristic(&localHeuristic); + + CbcBranchDefaultDecision branchDecision; + model.setBranchingMethod(&branchDecision); + + CbcCompareDefault nodeComparison; + model.setNodeComparison(nodeComparison); + + model.initialSolve(); + + if (model.isInitialSolveProvenPrimalInfeasible()) { + return kCbcUnsat; + } + if (model.isInitialSolveAbandoned()) { + return kCbcTimeout; + } + + model.setMinimumDrop(CoinMin(1.0, fabs(model.getMinimizationObjValue()) * 1.0e-3 + 1.0e-4)); + model.setMaximumCutPassesAtRoot(-100); + model.setNumberStrong(10); + model.solver()->setIntParam(OsiMaxNumIterationHotStart, 100); + + model.branchAndBound(); + + if (model.isProvenInfeasible()) { + return kCbcUnsat; + } + if (model.isProvenDualInfeasible()) { + return kCbcSolutionUnbounded; + } + if (model.isProvenOptimal()) { + value = model.getMinimizationObjValue(); + return kCbcSolutionBounded; + } + if (model.maximumSecondsReached()) { + return kCbcTimeout; + } + return kCbcAbandoned; +} + +void ThrowException(JNIEnv *env, const char *message) { + jclass exceptionClass = env->FindClass(kCbcExceptionClassName); + if (exceptionClass == nullptr) { + std::cerr << "WARNING: " << kCbcExceptionClassName << " class was not found" << std::endl; + exceptionClass = env->FindClass(kRuntimeExceptionClassName); + if (exceptionClass == nullptr) { + std::cerr << "FATAL: " << kRuntimeExceptionClassName << " class was not found" << std::endl; + std::cerr << "FATAL: " << message << std::endl; + std::exit(EXIT_FAILURE); + } + } + if (env->ThrowNew(exceptionClass, message) < 0) { + std::cerr << "FATAL: Could not throw java exception" << std::endl; + std::cerr << "FATAL: " << message << std::endl; + std::exit(EXIT_FAILURE); + } +} diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.hpp b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.hpp new file mode 100644 index 00000000..c65f71e3 --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/cpp/viatracbc.hpp @@ -0,0 +1,16 @@ +#ifndef HU_BME_MIT_INF_DSLREASONER_ILP_CBC_ +#define HU_BME_MIT_INF_DSLREASONER_ILP_CBC_ + +#include + +extern "C" { + +JNIEXPORT jint JNICALL Java_hu_bme_mit_inf_dslreasoner_ilp_cbc_CbcSolver_solveIlpProblem( + JNIEnv *env, jclass klazz, jdoubleArray columnLowerBoundsArray, jdoubleArray columnUpperBoundsArray, + jintArray rowStartsArray, jintArray columnIndicesArray, jdoubleArray entriesArray, + jdoubleArray rowLowerBoundsArray, jdoubleArray rowUpperBoundsArray, jdoubleArray objectiveArray, + jdoubleArray outputArray, jdouble timeoutSeconds, jboolean silent); + +} + +#endif // HU_BME_MIT_INF_DSLREASONER_ILP_CBC_ diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/lib/libviatracbc.so b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/lib/libviatracbc.so new file mode 100755 index 00000000..21fd2ff2 Binary files /dev/null and b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/lib/libviatracbc.so differ diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcException.java b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcException.java new file mode 100644 index 00000000..26846958 --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcException.java @@ -0,0 +1,30 @@ +package hu.bme.mit.inf.dslreasoner.ilp.cbc; + +public class CbcException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 2691773509078511887L; + + public CbcException() { + super(); + } + + public CbcException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public CbcException(String message, Throwable cause) { + super(message, cause); + } + + public CbcException(String message) { + super(message); + } + + public CbcException(Throwable cause) { + super(cause); + } + +} diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcResult.java b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcResult.java new file mode 100644 index 00000000..dae3a447 --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcResult.java @@ -0,0 +1,54 @@ +package hu.bme.mit.inf.dslreasoner.ilp.cbc; + +public abstract class CbcResult { + public static final CbcResult SOLUTION_UNBOUNDED = new CbcResult() { + }; + + public static final CbcResult UNSAT = new CbcResult() { + }; + + public static final CbcResult ABANDONED = new CbcResult() { + }; + + public static final CbcResult TIMEOUT = new CbcResult() { + }; + + private CbcResult() { + } + + public static class SolutionBounded extends CbcResult { + public final double value; + + public SolutionBounded(double value) { + this.value = value; + } + + public double getValue() { + return value; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(value); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SolutionBounded other = (SolutionBounded) obj; + if (Double.doubleToLongBits(value) != Double.doubleToLongBits(other.value)) + return false; + return true; + } + } +} diff --git a/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcSolver.java b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcSolver.java new file mode 100644 index 00000000..39b9d537 --- /dev/null +++ b/Solvers/ILP-Solver/hu.bme.mit.inf.dslreasoner.ilp.cbc/src/hu/bme/mit/inf/dslreasoner/ilp/cbc/CbcSolver.java @@ -0,0 +1,71 @@ +package hu.bme.mit.inf.dslreasoner.ilp.cbc; + +public class CbcSolver { + private static int CBC_SOLUTION_BOUNDED = 0; + private static int CBC_SOLUTION_UNBOUNDED = 1; + private static int CBC_UNSAT = 2; + private static int CBC_ABANDONED = 3; + private static int CBC_TIMEOUT = 4; + private static int CBC_ERROR = 5; + + private static boolean nativesLoaded = false; + + private CbcSolver() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly."); + } + + public static CbcResult solve(double[] columnLowerBounds, double[] columnUpperBounds, int[] rowStarts, + int[] columnIndices, double[] entries, double[] rowLowerBounds, double[] rowUpperBounds, + double[] objective, double timeoutSeconds, boolean silent) { + loadNatives(); + validate(columnLowerBounds, columnUpperBounds, rowStarts, columnIndices, entries, rowLowerBounds, + rowUpperBounds, objective); + double[] output = new double[1]; + int result = solveIlpProblem(columnLowerBounds, columnUpperBounds, rowStarts, columnIndices, entries, + rowLowerBounds, rowUpperBounds, objective, output, timeoutSeconds, silent); + if (result == CBC_SOLUTION_BOUNDED) { + return new CbcResult.SolutionBounded(output[0]); + } else if (result == CBC_SOLUTION_UNBOUNDED) { + return CbcResult.SOLUTION_UNBOUNDED; + } else if (result == CBC_UNSAT) { + return CbcResult.UNSAT; + } else if (result == CBC_ABANDONED) { + return CbcResult.ABANDONED; + } else if (result == CBC_TIMEOUT) { + return CbcResult.TIMEOUT; + } else if (result == CBC_ERROR) { + throw new CbcException("Solver signalled error, but no exception was thrown"); + } else { + throw new CbcException("Unknown return value: " + result); + } + } + + private static void loadNatives() { + if (!nativesLoaded) { + synchronized (CbcSolver.class) { + System.loadLibrary("viatracbc"); + nativesLoaded = true; + } + } + } + + private static void validate(double[] columnLowerBounds, double[] columnUpperBounds, int[] rowStarts, + int[] columnIndices, double[] entries, double[] rowLowerBounds, double[] rowUpperBounds, + double[] objective) { + int numColumns = columnLowerBounds.length; + if (columnUpperBounds.length != numColumns) { + throw new CbcException("Lengths of columnLowerBounds and columnUpperBounds must match"); + } + if (objective.length != numColumns) { + throw new CbcException("Lengths of columnLowerBounds and objective must match"); + } + int numRows = rowLowerBounds.length; + if (rowUpperBounds.length != numRows) { + throw new CbcException("Lengths of rowLowerBounds and rowUpperBounds must match"); + } + } + + private static native int solveIlpProblem(double[] columnLowerBounds, double[] columnUpperBounds, int[] rowStarts, + int[] columnIndices, double[] entries, double[] rowLowerBounds, double[] rowUpperBounds, double[] objective, + double[] output, double timeoutSeconds, boolean silent); +} -- cgit v1.2.3-70-g09d2