1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
on-restart-benchmarks/solvers/MIP/MIP_scip_wrap.cpp
Jip J. Dekker f2a1c4e389 Squashed 'software/mza/' content from commit f970a59b17
git-subtree-dir: software/mza
git-subtree-split: f970a59b177c13ca3dd8aaef8cc6681d83b7e813
2021-07-11 16:34:30 +10:00

582 lines
20 KiB
C++

// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Gleb Belov <gleb.belov@monash.edu>
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <cmath>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
using namespace std;
#include <minizinc/solvers/MIP/MIP_scip_wrap.hh>
#include <minizinc/utils.hh>
#include "scip/scipshell.h"
std::string MIP_scip_wrapper::getMznLib() { return "-Glinear_scip"; }
string MIP_scip_wrapper::getDescription(MiniZinc::SolverInstanceBase::Options* opt) {
ostringstream oss;
oss << "MIP wrapper for SCIP " << getVersion(opt) << ". Compiled " __DATE__ " " __TIME__;
return oss.str();
}
string MIP_scip_wrapper::getVersion(MiniZinc::SolverInstanceBase::Options* opt) {
ostringstream oss;
oss << SCIPmajorVersion() << '.' << SCIPminorVersion() << '.' << SCIPtechVersion() << '.'
<< SCIPsubversion();
return oss.str();
}
string MIP_scip_wrapper::needDllFlag() { return ""; }
string MIP_scip_wrapper::getId() { return "scip"; }
string MIP_scip_wrapper::getName() { return "SCIP"; }
vector<string> MIP_scip_wrapper::getTags() { return {"mip", "float", "api"}; }
vector<string> MIP_scip_wrapper::getStdFlags() { return {"-a", "-p", "-s"}; }
void MIP_scip_wrapper::Options::printHelp(ostream& os) {
os << "SCIP MIP wrapper options:"
<< std::endl
// -s print statistics
// << " --readParam <file> read SCIP parameters from file
// << "--writeParam <file> write SCIP parameters to file
// << "--tuneParam instruct SCIP to tune parameters instead of solving
<< "--writeModel <file> write model to <file> (.lp, .mps, ...?)" << std::endl
<< "-a print intermediate solutions (use for optimization problems only TODO)"
<< std::endl
<< "-p <N> use N threads, default: 1"
<< std::endl
// << "--nomippresolve disable MIP presolving NOT IMPL" << std::endl
<< "--solver-time-limit <N> stop search after N milliseconds" << std::endl
<< "--workmem <N> maximal amount of RAM used, MB" << std::endl
<< "--readParam <file> read SCIP parameters from file" << std::endl
<< "--writeParam <file> write SCIP parameters to file"
<< std::endl
// << "--tuneParam instruct SCIP to tune parameters instead of solving NOT IMPL"
<< "--absGap <n> absolute gap |primal-dual| to stop" << std::endl
<< "--relGap <n> relative gap |primal-dual|/<solver-dep> to stop. Default 1e-8, set <0 "
"to use backend's default"
<< std::endl
<< "--intTol <n> integrality tolerance for a variable. Default 1e-8"
<< std::endl
// << "--objDiff <n> objective function discretization. Default 1.0" << std::endl
<< std::endl;
}
static inline bool beginswith(string s, string t) { return s.compare(0, t.length(), t) == 0; }
bool MIP_scip_wrapper::Options::processOption(int& i, vector<string>& argv) {
MiniZinc::CLOParser cop(i, argv);
if (string(argv[i]) == "-a" || string(argv[i]) == "--all" ||
string(argv[i]) == "--all-solutions") {
flag_all_solutions = true;
} else if (string(argv[i]) == "-f") {
// std::cerr << " Flag -f: ignoring fixed strategy anyway." << std::endl;
} else if (cop.get("--writeModel", &sExportModel)) {
} else if (cop.get("-p", &nThreads)) {
} else if (cop.get("--solver-time-limit", &nTimeout)) {
} else if (cop.get("--workmem", &nWorkMemLimit)) {
} else if (cop.get("--readParam", &sReadParams)) {
} else if (cop.get("--writeParam", &sWriteParams)) {
} else if (cop.get("--absGap", &absGap)) {
} else if (cop.get("--relGap", &relGap)) {
} else if (cop.get("--intTol", &intTol)) {
// } else if ( cop.get( "--objDiff", &objDiff ) ) {
} else
return false;
return true;
error:
return false;
}
void MIP_scip_wrapper::wrap_assert(SCIP_RETCODE retcode, string msg, bool fTerm) {
/* evaluate return code of the SCIP process */
if (retcode != SCIP_OKAY) {
/* write error back trace */
SCIPprintError(retcode);
string msgAll = (" MIP_scip_wrapper runtime error, see output: " + msg);
cerr << msgAll << endl;
if (fTerm) {
cerr << "TERMINATING." << endl;
throw runtime_error(msgAll);
}
}
}
SCIP_RETCODE MIP_scip_wrapper::openSCIP() {
SCIP_CALL(SCIPcreate(&scip));
SCIP_CALL(SCIPincludeDefaultPlugins(scip));
/* create empty problem */
SCIP_CALL(SCIPcreateProbBasic(scip, "mzn_scip"));
return SCIP_OKAY;
}
SCIP_RETCODE MIP_scip_wrapper::closeSCIP() {
SCIP_CALL(SCIPfree(&scip));
/// and at last:
// MIP_wrapper::cleanup();
return SCIP_OKAY;
}
SCIP_RETCODE MIP_scip_wrapper::doAddVars_SCIP(size_t n, double* obj, double* lb, double* ub,
MIP_wrapper::VarType* vt, string* names) {
/// Convert var types:
// vector<char> ctype(n);
// vector<char*> pcNames(n);
for (size_t j = 0; j < n; ++j) {
// pcNames[i] = (char*)names[i].c_str();
SCIP_VARTYPE ctype;
switch (vt[j]) {
case REAL:
ctype = SCIP_VARTYPE_CONTINUOUS;
break;
case INT:
ctype = SCIP_VARTYPE_INTEGER;
break;
case BINARY:
ctype = SCIP_VARTYPE_BINARY;
break;
default:
throw runtime_error(" MIP_wrapper: unknown variable type");
}
scipVars.resize(scipVars.size() + 1);
if (fPhase1Over) assert(scipVars.size() == colObj.size());
SCIP_CALL(
SCIPcreateVarBasic(scip, &scipVars.back(), names[j].c_str(), lb[j], ub[j], obj[j], ctype));
SCIP_CALL(SCIPaddVar(scip, scipVars.back()));
}
// retcode = SCIP_newcols (env, lp, n, obj, lb, ub, &ctype[0], &pcNames[0]);
// wrap_assert( !retcode, "Failed to declare variables." );
return SCIP_OKAY;
}
SCIP_RETCODE MIP_scip_wrapper::delSCIPVars() {
for (size_t j = 0; j < scipVars.size(); ++j) SCIPreleaseVar(scip, &scipVars[j]);
return SCIP_OKAY;
}
SCIP_RETCODE MIP_scip_wrapper::addRow_SCIP(int nnz, int* rmatind, double* rmatval,
MIP_wrapper::LinConType sense, double rhs, int mask,
string rowName) {
/// Convert var types:
double lh = -SCIPinfinity(scip), rh = SCIPinfinity(scip);
switch (sense) {
case LQ:
rh = rhs;
break;
case EQ:
lh = rh = rhs;
break;
case GQ:
lh = rhs;
break;
default:
throw runtime_error(" MIP_wrapper: unknown constraint type");
}
const int ccnt = 0;
const int rcnt = 1;
const int rmatbeg[] = {0};
char* pRName = (char*)rowName.c_str();
// ignoring mask for now. TODO
SCIP_CONS* cons;
vector<SCIP_VAR*> ab(nnz);
for (int j = 0; j < nnz; ++j) ab[j] = scipVars[rmatind[j]];
SCIP_CALL(SCIPcreateConsBasicLinear(scip, &cons, rowName.c_str(), nnz, &ab[0], rmatval, lh, rh));
SCIP_CALL(SCIPaddCons(scip, cons));
SCIP_CALL(SCIPreleaseCons(scip, &cons));
return SCIP_OKAY;
// retcode = SCIP_addrows (env, lp, ccnt, rcnt, nnz, &rhs,
// &ssense, rmatbeg, rmatind, rmatval,
// NULL, &pRName);
// wrap_assert( !retcode, "Failed to add constraint." );
}
void MIP_scip_wrapper::setVarBounds(int iVar, double lb, double ub) {
wrap_assert(lb <= ub ? SCIP_OKAY : SCIP_ERROR, "scip interface: setVarBounds: lb>ub");
setVarLB(iVar, lb);
setVarUB(iVar, ub);
}
void MIP_scip_wrapper::setVarLB(int iVar, double lb) {
auto res = SCIPchgVarLbGlobal(scip, scipVars[iVar], lb);
wrap_assert(res, "scip interface: failed to set var lb.");
}
void MIP_scip_wrapper::setVarUB(int iVar, double ub) {
auto res = SCIPchgVarUbGlobal(scip, scipVars[iVar], ub);
wrap_assert(res, "scip interface: failed to set var ub.");
}
void MIP_scip_wrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind,
double* rmatval, MIP_wrapper::LinConType sense,
double rhs, string rowName) {
MZN_ASSERT_HARD_MSG(0 <= bVal && 1 >= bVal, "SCIP: addIndicatorConstraint: bVal not 0/1");
//// Make sure in order to notice the indices of lazy constr: also here? TODO
// ++ nRows;
SCIP_CONS* cons;
vector<SCIP_VAR*> ab(nnz);
SCIP_VAR*
indicator_var; // SCIP 6.0.1 requires that the implication is active for indicator_x == 1
for (int j = 0; j < nnz; ++j) ab[j] = scipVars[rmatind[j]];
indicator_var = scipVars[iBVar];
if (0 == bVal) {
wrap_assert(SCIPgetNegatedVar(scip, indicator_var, &indicator_var));
}
if (LQ == sense || EQ == sense) {
wrap_assert(SCIPcreateConsBasicIndicator(scip, &cons, rowName.c_str(), indicator_var, nnz,
ab.data(), rmatval, rhs));
wrap_assert(SCIPaddCons(scip, cons));
wrap_assert(SCIPreleaseCons(scip, &cons));
}
if (GQ == sense || EQ == sense) {
std::vector<double> rmatvalNEG(nnz);
for (int i = nnz; i--;) rmatvalNEG[i] = -rmatval[i];
wrap_assert(SCIPcreateConsBasicIndicator(scip, &cons, rowName.c_str(), indicator_var, nnz,
ab.data(), rmatvalNEG.data(), -rhs));
wrap_assert(SCIPaddCons(scip, cons));
wrap_assert(SCIPreleaseCons(scip, &cons));
}
}
void MIP_scip_wrapper::addBoundsDisj(int n, double* fUB, double* bnd, int* vars, int nF,
double* fUBF, double* bndF, int* varsF, string rowName) {
SCIP_CONS* cons;
std::vector<SCIP_VAR*> v(n + nF);
std::vector<SCIP_BOUNDTYPE> bt(n + nF);
std::vector<SCIP_Real> bs(n + nF);
for (int j = 0; j < n; ++j) {
v[j] = scipVars[vars[j]];
bt[j] = fUB[j] ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER;
bs[j] = bnd[j];
}
for (int j = 0; j < nF; ++j) {
v[n + j] = scipVars[varsF[j]];
bt[n + j] = fUBF[j] ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER;
bs[n + j] = bndF[j];
}
wrap_assert(SCIPcreateConsBasicBounddisjunction(scip, &cons, rowName.c_str(), v.size(), v.data(),
bt.data(), bs.data()));
wrap_assert(SCIPaddCons(scip, cons));
wrap_assert(SCIPreleaseCons(scip, &cons));
}
void MIP_scip_wrapper::addCumulative(int nnz, int* rmatind, double* d, double* r, double b,
string rowName) {
SCIP_CONS* cons;
vector<SCIP_VAR*> ab(nnz);
vector<int> nd(nnz), nr(nnz);
for (int j = 0; j < nnz; ++j) {
ab[j] = scipVars[rmatind[j]];
nd[j] = (int)round(d[j]);
nr[j] = (int)round(r[j]);
}
wrap_assert(SCIPcreateConsBasicCumulative(scip, &cons, rowName.c_str(), nnz, ab.data(), nd.data(),
nr.data(), (int)round(b)));
wrap_assert(SCIPaddCons(scip, cons));
wrap_assert(SCIPreleaseCons(scip, &cons));
}
/// SolutionCallback ------------------------------------------------------------------------
/// From event_bestsol.c:
#define EVENTHDLR_NAME "bestsol"
#define EVENTHDLR_DESC "event handler for best solutions found"
/** includes event handler for best solution found */
extern SCIP_RETCODE SCIPincludeEventHdlrBestsol(SCIP* scip /**< SCIP data structure */
);
/** copy method for event handler plugins (called when SCIP copies plugins) */
static SCIP_DECL_EVENTCOPY(eventCopyBestsol) { /*lint --e{715}*/
assert(scip != NULL);
assert(eventhdlr != NULL);
assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
/* call inclusion method of event handler */
SCIP_CALL(SCIPincludeEventHdlrBestsol(scip));
return SCIP_OKAY;
}
/** initialization method of event handler (called after problem was transformed) */
static SCIP_DECL_EVENTINIT(eventInitBestsol) { /*lint --e{715}*/
assert(scip != NULL);
assert(eventhdlr != NULL);
assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
/* notify SCIP that your event handler wants to react on the event type best solution found */
SCIP_CALL(SCIPcatchEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, eventhdlr, NULL, NULL));
return SCIP_OKAY;
}
/** deinitialization method of event handler (called before transformed problem is freed) */
static SCIP_DECL_EVENTEXIT(eventExitBestsol) { /*lint --e{715}*/
assert(scip != NULL);
assert(eventhdlr != NULL);
assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
/* notify SCIP that your event handler wants to drop the event type best solution found */
SCIP_CALL(SCIPdropEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, eventhdlr, NULL, -1));
return SCIP_OKAY;
}
static MIP_wrapper::CBUserInfo* cbuiPtr = 0;
static SCIP_VAR** scipVarsPtr = 0;
/** execution method of event handler */
static SCIP_DECL_EVENTEXEC(eventExecBestsol) { /*lint --e{715}*/
SCIP_SOL* bestsol;
SCIP_Real objVal;
int newincumbent = 0;
assert(eventhdlr != NULL);
assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0);
assert(event != NULL);
assert(scip != NULL);
assert(SCIPeventGetType(event) == SCIP_EVENTTYPE_BESTSOLFOUND);
SCIPdebugMessage("exec method of event handler for best solution found\n");
bestsol = SCIPgetBestSol(scip);
assert(bestsol != NULL);
objVal = SCIPgetSolOrigObj(scip, bestsol);
if (!cbuiPtr) return SCIP_OKAY;
if (fabs(cbuiPtr->pOutput->objVal - objVal) > 1e-12 * (1.0 + fabs(objVal))) {
newincumbent = 1;
cbuiPtr->pOutput->objVal = objVal;
cbuiPtr->pOutput->status = MIP_wrapper::SAT;
cbuiPtr->pOutput->statusName = "feasible from a callback";
}
if (newincumbent && scipVarsPtr) {
assert(cbuiPtr->pOutput->x);
SCIP_CALL(SCIPgetSolVals(scip, bestsol, cbuiPtr->pOutput->nCols, scipVarsPtr,
(double*)cbuiPtr->pOutput->x));
// wrap_assert(!retcode, "Failed to get variable values.");
cbuiPtr->pOutput->nNodes = SCIPgetNNodes(scip);
cbuiPtr->pOutput->nOpenNodes = SCIPgetNNodesLeft(scip);
cbuiPtr->pOutput->bestBound = SCIPgetDualbound(scip);
cbuiPtr->pOutput->dCPUTime = -1;
/// Call the user function:
if (cbuiPtr->solcbfn) (*cbuiPtr->solcbfn)(*cbuiPtr->pOutput, cbuiPtr->psi);
}
return SCIP_OKAY;
}
/** includes event handler for best solution found */
SCIP_RETCODE SCIPincludeEventHdlrBestsol(SCIP* scip /**< SCIP data structure */
) {
SCIP_EVENTHDLRDATA* eventhdlrdata;
SCIP_EVENTHDLR* eventhdlr;
eventhdlrdata = NULL;
eventhdlr = NULL;
/* create event handler for events on watched variables */
SCIP_CALL(SCIPincludeEventhdlrBasic(scip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC,
eventExecBestsol, eventhdlrdata));
assert(eventhdlr != NULL);
/// Not for sub-SCIPs
// SCIP_CALL( SCIPsetEventhdlrCopy(scip, eventhdlr, eventCopyBestsol) );
SCIP_CALL(SCIPsetEventhdlrInit(scip, eventhdlr, eventInitBestsol));
SCIP_CALL(SCIPsetEventhdlrExit(scip, eventhdlr, eventExitBestsol));
return SCIP_OKAY;
}
MIP_scip_wrapper::Status MIP_scip_wrapper::convertStatus(SCIP_STATUS scipStatus) {
Status s = Status::UNKNOWN;
/* Converting the status. */
switch (scipStatus) {
case SCIP_STATUS_OPTIMAL:
s = Status::OPT;
output.statusName = "Optimal";
assert(SCIPgetNSolsFound(scip));
break;
case SCIP_STATUS_INFEASIBLE:
s = Status::UNSAT;
output.statusName = "Infeasible";
break;
// case SCIP_MIP_OPTIMAL_INFEAS:
case SCIP_STATUS_INFORUNBD:
s = Status::UNSATorUNBND;
output.statusName = "Infeasible or unbounded";
break;
// case SCIP_MIP_SOL_LIM:
// s = Status::SAT;
// wrap_assert(SCIP_getsolnpoolnumsolns(env, lp), "Feasibility reported but pool
// empty?", false); break;
case SCIP_STATUS_UNBOUNDED:
s = Status::UNBND;
output.statusName = "Unbounded";
break;
// case SCIP_STATUSMIP_ABORT_INFEAS:
// case SCIP_MIP_FAIL_INFEAS:
// s = Status::ERROR;
// break;
default:
// case SCIP_MIP_OPTIMAL_TOL:
// case SCIP_MIP_ABORT_RELAXATION_UNBOUNDED:
if (SCIPgetNSols(scip)) {
s = Status::SAT;
output.statusName = "Feasible";
} else {
s = Status::UNKNOWN;
output.statusName = "Unknown";
}
}
return s;
}
SCIP_DECL_MESSAGEWARNING(printMsg) { cerr << msg << flush; }
SCIP_RETCODE MIP_scip_wrapper::solve_SCIP() { // Move into ancestor?
/////////////// Last-minute solver options //////////////////
if (options->flag_all_solutions && 0 == nProbType)
cerr << "WARNING. --all-solutions for SAT problems not implemented." << endl;
if (options->nThreads > 0) SCIP_CALL(SCIPsetIntParam(scip, "lp/threads", options->nThreads));
if (options->nTimeout > 0)
SCIP_CALL(
SCIPsetRealParam(scip, "limits/time", static_cast<double>(options->nTimeout) / 1000.0));
if (options->nWorkMemLimit > 0)
SCIP_CALL(SCIPsetRealParam(scip, "limits/memory", options->nWorkMemLimit));
if (options->absGap >= 0.0) SCIP_CALL(SCIPsetRealParam(scip, "limits/absgap", options->absGap));
if (options->relGap >= 0.0) SCIP_CALL(SCIPsetRealParam(scip, "limits/gap", options->relGap));
if (options->intTol >= 0.0)
SCIP_CALL(SCIPsetRealParam(scip, "numerics/feastol", options->intTol));
// retcode = SCIP_setintparam (env, SCIP_PARAM_ClockType, 1); // CPU time
// wrap_assert(!retcode, " SCIP Warning: Failure to measure CPU time.", false);
if (!options->sExportModel.empty()) {
// std::cerr <<" Exporting LP model to " << sExportModel << " ..." << std::endl;
SCIP_CALL(SCIPwriteOrigProblem(scip, options->sExportModel.c_str(), 0, 0));
}
/* Turn on output to the screen - after model export */
if (!fVerbose) {
// SCIP_CALL(SCIPsetMessagehdlr(scip, NULL)); No LP export then
SCIPsetMessagehdlrQuiet(scip, true);
} else {
SCIP_MESSAGEHDLR* pHndl = 0;
SCIP_CALL(SCIPmessagehdlrCreate(&pHndl, FALSE, NULL, FALSE, printMsg, printMsg, printMsg, NULL,
NULL));
SCIP_CALL(SCIPsetMessagehdlr(scip, pHndl));
}
// assert(scipVars.size() == colObj.size());
int cur_numcols = scipVars.size(); // No, we create negated indicators: getNCols();
assert(cur_numcols == colObj.size());
assert(cur_numcols == scipVars.size());
/// Solution callback
output.nCols = colObj.size();
x.resize(output.nCols);
output.x = &x[0];
if (options->flag_all_solutions && cbui.solcbfn && !cbuiPtr) {
/* include event handler for best solution found */
SCIP_CALL(SCIPincludeEventHdlrBestsol(scip));
cbuiPtr = &cbui; // not thread-safe... TODO
scipVarsPtr = &scipVars[0];
// retcode = SCIP_setinfocallbackfunc (env, solcallback, &cbui);
// wrap_assert(!retcode, "Failed to set solution callback", false);
}
if (options->sReadParams.size()) {
SCIP_CALL(SCIPreadParams(scip, options->sReadParams.c_str()));
}
if (options->sWriteParams.size()) {
SCIP_CALL(SCIPwriteParams(scip, options->sReadParams.c_str(), TRUE, FALSE));
}
cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now();
output.dCPUTime = clock();
/* Optimize the problem and obtain solution. */
SCIP_CALL(SCIPsolve(scip));
// wrap_assert( !retcode, "Failed to optimize MIP." );
output.dWallTime =
std::chrono::duration<double>(std::chrono::steady_clock::now() - output.dWallTime0).count();
output.dCPUTime = (clock() - output.dCPUTime) / CLOCKS_PER_SEC;
cbuiPtr = 0; /// cleanup
scipVarsPtr = 0;
SCIP_STATUS solstat = SCIPgetStatus(scip);
output.status = convertStatus(solstat);
// output.statusName = SCIP_getstatstring (env, solstat, scip_status_buffer);
/// Continuing to fill the output object:
output.objVal = SCIPgetPrimalbound(scip);
output.bestBound = SCIPgetDualbound(scip);
// wrap_assert(!retcode, "Failed to get the best bound.", false);
if (Status::OPT == output.status || Status::SAT == output.status) {
// wrap_assert( !retcode, "No MIP objective value available." );
x.resize(cur_numcols);
output.x = &x[0];
SCIP_CALL(
SCIPgetSolVals(scip, SCIPgetBestSol(scip), cur_numcols, &scipVars[0], (double*)output.x));
if (cbui.solcbfn && (!options->flag_all_solutions || !cbui.printed)) {
cbui.solcbfn(output, cbui.psi);
}
}
output.nNodes = SCIPgetNNodes(scip);
output.nOpenNodes = SCIPgetNNodesLeft(scip); // SCIP_getnodeleftcnt (env, lp);
SCIP_CALL(SCIPfreeTransform(scip));
return SCIP_OKAY;
}
SCIP_RETCODE MIP_scip_wrapper::setObjSense_SCIP(int s) {
SCIP_CALL(SCIPsetObjsense(scip, s > 0 ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
return SCIP_OKAY;
}