Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
xpress_interface.cc
Go to the documentation of this file.
1// Copyright 2019-2023 RTE
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// Initial version of this code was provided by RTE
15
16#include <algorithm>
17#include <clocale>
18#include <fstream>
19#include <istream>
20#include <limits>
21#include <memory>
22#include <mutex>
23#include <string>
24
25#include "absl/strings/str_format.h"
27#include "ortools/base/timer.h"
30
31#define XPRS_INTEGER 'I'
32#define XPRS_CONTINUOUS 'C'
33
34// The argument to this macro is the invocation of a XPRS function that
35// returns a status. If the function returns non-zero the macro aborts
36// the program with an appropriate error message.
37#define CHECK_STATUS(s) \
38 do { \
39 int const status_ = s; \
40 CHECK_EQ(0, status_); \
41 } while (0)
42
43namespace operations_research {
44std::string getSolverVersion(XPRSprob const& prob) {
45 // XPRS_VERSION gives the version number as MAJOR*100 + RELEASE.
46 // It does not include the build number.
47 int version;
48 if (!prob || XPRSgetintcontrol(prob, XPRS_VERSION, &version))
49 return "XPRESS library version unknown";
50
51 int const major = version / 100;
52 version -= major * 100;
53 int const release = version;
54
55 return absl::StrFormat("XPRESS library version %d.%02d", major, release);
56}
57
58// Apply the specified name=value setting to prob.
59bool readParameter(XPRSprob const& prob, std::string const& name,
60 std::string const& value) {
61 // We cannot set empty parameters.
62 if (!value.size()) {
63 LOG(DFATAL) << "Empty value for parameter '" << name << "' in "
64 << getSolverVersion(prob);
65 return false;
66 }
67
68 // Figure out the type of the control.
69 int id, type;
70 if (XPRSgetcontrolinfo(prob, name.c_str(), &id, &type) ||
71 type == XPRS_TYPE_NOTDEFINED) {
72 LOG(DFATAL) << "Unknown parameter '" << name << "' in "
73 << getSolverVersion(prob);
74 return false;
75 }
76
77 // Depending on the type, parse the text in value and apply it.
78 std::stringstream v(value);
79 v.imbue(std::locale("C"));
80 switch (type) {
81 case XPRS_TYPE_INT: {
82 int i;
83 v >> i;
84 if (!v.eof()) {
85 LOG(DFATAL) << "Failed to parse value '" << value
86 << "' for int parameter '" << name << "' in "
87 << getSolverVersion(prob);
88 return false;
89 }
90 if (XPRSsetintcontrol(prob, id, i)) {
91 LOG(DFATAL) << "Failed to set int parameter '" << name << "' to "
92 << value << " (" << i << ") in " << getSolverVersion(prob);
93 return false;
94 }
95 } break;
96 case XPRS_TYPE_INT64: {
97 XPRSint64 i;
98 v >> i;
99 if (!v.eof()) {
100 LOG(DFATAL) << "Failed to parse value '" << value
101 << "' for int64_t parameter '" << name << "' in "
102 << getSolverVersion(prob);
103 return false;
104 }
105 if (XPRSsetintcontrol64(prob, id, i)) {
106 LOG(DFATAL) << "Failed to set int64_t parameter '" << name << "' to "
107 << value << " (" << i << ") in " << getSolverVersion(prob);
108 return false;
109 }
110 } break;
111 case XPRS_TYPE_DOUBLE: {
112 double d;
113 v >> d;
114 if (!v.eof()) {
115 LOG(DFATAL) << "Failed to parse value '" << value
116 << "' for dbl parameter '" << name << "' in "
117 << getSolverVersion(prob);
118 return false;
119 }
120 if (XPRSsetdblcontrol(prob, id, d)) {
121 LOG(DFATAL) << "Failed to set double parameter '" << name << "' to "
122 << value << " (" << d << ") in " << getSolverVersion(prob);
123 return false;
124 }
125 } break;
126 default:
127 // Note that string parameters are not supported at the moment since
128 // we don't want to deal with potential encoding or escaping issues.
129 LOG(DFATAL) << "Unsupported parameter type " << type << " for parameter '"
130 << name << "' in " << getSolverVersion(prob);
131 return false;
132 }
133
134 return true;
135}
136
137void printError(const XPRSprob& mLp, int line) {
138 char errmsg[512];
139 XPRSgetlasterror(mLp, errmsg);
140 VLOG(0) << absl::StrFormat("Function line %d did not execute correctly: %s\n",
141 line, errmsg);
142 exit(0);
143}
144
145void XPRS_CC XpressIntSolCallbackImpl(XPRSprob cbprob, void* cbdata);
146
147/**********************************************************************************\
148* Name: optimizermsg *
149* Purpose: Display Optimizer error messages and warnings. *
150* Arguments: const char *sMsg Message string *
151* int nLen Message length *
152* int nMsgLvl Message type *
153* Return Value: None *
154\**********************************************************************************/
155void XPRS_CC optimizermsg(XPRSprob prob, void* data, const char* sMsg, int nLen,
156 int nMsgLvl);
157
158int getnumcols(const XPRSprob& mLp) {
159 int nCols = 0;
160 XPRSgetintattrib(mLp, XPRS_COLS, &nCols);
161 return nCols;
162}
163
164int getnumrows(const XPRSprob& mLp) {
165 int nRows = 0;
166 XPRSgetintattrib(mLp, XPRS_ROWS, &nRows);
167 return nRows;
168}
169
170int getitcnt(const XPRSprob& mLp) {
171 int nIters = 0;
172 XPRSgetintattrib(mLp, XPRS_SIMPLEXITER, &nIters);
173 return nIters;
174}
175
176int getnodecnt(const XPRSprob& mLp) {
177 int nNodes = 0;
178 XPRSgetintattrib(mLp, XPRS_NODES, &nNodes);
179 return nNodes;
180}
181
182int setobjoffset(const XPRSprob& mLp, double value) {
183 // TODO detect xpress version
184 static int indexes[1] = {-1};
185 double values[1] = {-value};
186 XPRSchgobj(mLp, 1, indexes, values);
187 return 0;
188}
189
190void addhint(const XPRSprob& mLp, int length, const double solval[],
191 const int colind[]) {
192 // The OR-Tools API does not allow setting a name for the solution
193 // passing NULL to XPRESS will have it generate a unique ID for the solution
194 if (int status = XPRSaddmipsol(mLp, length, solval, colind, NULL)) {
195 LOG(WARNING) << "Failed to set solution hint.";
196 }
197}
198
200
202 // Reason values below 1000 are reserved by XPRESS
203 XPRSinterrupt(xprsProb, 1000 + reason);
204}
205
206// In case we need to return a double but don't have a value for that
207// we just return a NaN.
208#if !defined(XPRS_NAN)
209#define XPRS_NAN std::numeric_limits<double>::quiet_NaN()
210#endif
211
212using std::unique_ptr;
213
215 friend class XpressInterface;
216
217 public:
219 int num_nodes)
220 : xprsprob_(xprsprob),
221 event_(event),
222 num_nodes_(num_nodes),
223 variable_values_(0) {};
224
225 // Implementation of the interface.
226 MPCallbackEvent Event() override { return event_; };
227 bool CanQueryVariableValues() override;
228 double VariableValue(const MPVariable* variable) override;
229 void AddCut(const LinearRange& cutting_plane) override {
230 LOG(WARNING) << "AddCut is not implemented yet in XPRESS interface";
231 };
232 void AddLazyConstraint(const LinearRange& lazy_constraint) override {
233 LOG(WARNING)
234 << "AddLazyConstraint is not implemented yet in XPRESS interface";
235 };
236 double SuggestSolution(
237 const absl::flat_hash_map<const MPVariable*, double>& solution) override;
238 int64_t NumExploredNodes() override { return num_nodes_; };
239
240 // Call this method to update the internal state of the callback context
241 // before passing it to MPCallback::RunCallback().
242 // Returns true if the internal state has changed.
244
245 private:
246 XPRSprob* xprsprob_;
247 MPCallbackEvent event_;
248 std::vector<double>
249 variable_values_; // same order as MPVariable* elements in MPSolver
250 int num_nodes_;
251};
252
253// Wraps the MPCallback in order to catch and store exceptions
255 public:
256 explicit MPCallbackWrapper(MPCallback* callback) : callback_(callback) {};
257 MPCallback* GetCallback() const { return callback_; }
258 // Since our (C++) call-back functions are called from the XPRESS (C) code,
259 // exceptions thrown in our call-back code are not caught by XPRESS.
260 // We have to catch them, interrupt XPRESS, and log them after XPRESS is
261 // effectively interrupted (ie after solve).
263 exceptions_mutex_.lock();
264 caught_exceptions_.push_back(std::current_exception());
266 exceptions_mutex_.unlock();
267 }
269 exceptions_mutex_.lock();
270 for (const std::exception_ptr& ex : caught_exceptions_) {
271 try {
272 std::rethrow_exception(ex);
273 } catch (std::exception& ex) {
274 // We don't want the interface to throw exceptions, plus it causes
275 // SWIG issues in Java & Python. Instead, we'll only log them.
276 // (The use cases where the user has to raise an exception inside their
277 // call-back does not seem to be frequent, anyway.)
278 LOG(ERROR) << "Caught exception during user-defined call-back: "
279 << ex.what();
280 }
281 }
282 caught_exceptions_.clear();
283 exceptions_mutex_.unlock();
284 };
285
286 private:
287 MPCallback* callback_;
288 std::vector<std::exception_ptr> caught_exceptions_;
289 std::mutex exceptions_mutex_;
290};
291
292// For a model that is extracted to an instance of this class there is a
293// 1:1 correspondence between MPVariable instances and XPRESS columns: the
294// index of an extracted variable is the column index in the XPRESS model.
295// Similar for instances of MPConstraint: the index of the constraint in
296// the model is the row index in the XPRESS model.
298 public:
299 // NOTE: 'mip' specifies the type of the problem (either continuous or
300 // mixed integer). This type is fixed for the lifetime of the
301 // instance. There are no dynamic changes to the model type.
302 explicit XpressInterface(MPSolver* solver, bool mip);
303 ~XpressInterface() override;
304
305 // Sets the optimization direction (min/max).
306 void SetOptimizationDirection(bool maximize) override;
307
308 // ----- Solve -----
309 // Solve the problem using the parameter values specified.
310 MPSolver::ResultStatus Solve(MPSolverParameters const& param) override;
311
312 // Writes the model.
313 void Write(const std::string& filename) override;
314
315 // ----- Model modifications and extraction -----
316 // Resets extracted model
317 void Reset() override;
318
319 void SetVariableBounds(int var_index, double lb, double ub) override;
320 void SetVariableInteger(int var_index, bool integer) override;
321 void SetConstraintBounds(int row_index, double lb, double ub) override;
322
323 void AddRowConstraint(MPConstraint* ct) override;
324 void AddVariable(MPVariable* var) override;
325 void SetCoefficient(MPConstraint* constraint, MPVariable const* variable,
326 double new_value, double old_value) override;
327
328 // Clear a constraint from all its terms.
329 void ClearConstraint(MPConstraint* constraint) override;
330 // Change a coefficient in the linear objective
331 void SetObjectiveCoefficient(MPVariable const* variable,
332 double coefficient) override;
333 // Change the constant term in the linear objective.
334 void SetObjectiveOffset(double value) override;
335 // Clear the objective from all its terms.
336 void ClearObjective() override;
337
338 // ------ Query statistics on the solution and the solve ------
339 // Number of simplex iterations
340 virtual int64_t iterations() const;
341 // Number of branch-and-bound nodes. Only available for discrete problems.
342 virtual int64_t nodes() const;
343
344 // Returns the basis status of a row.
345 MPSolver::BasisStatus row_status(int constraint_index) const override;
346 // Returns the basis status of a column.
347 MPSolver::BasisStatus column_status(int variable_index) const override;
348
349 // ----- Misc -----
350
351 // Query problem type.
352 // Remember that problem type is a static property that is set
353 // in the constructor and never changed.
354 bool IsContinuous() const override { return IsLP(); }
355 bool IsLP() const override { return !mMip; }
356 bool IsMIP() const override { return mMip; }
357
359 const std::vector<MPSolver::BasisStatus>& variable_statuses,
360 const std::vector<MPSolver::BasisStatus>& constraint_statuses) override;
361
362 void ExtractNewVariables() override;
363 void ExtractNewConstraints() override;
364 void ExtractObjective() override;
365
366 std::string SolverVersion() const override;
367
368 void* underlying_solver() override { return reinterpret_cast<void*>(mLp); }
369
370 double ComputeExactConditionNumber() const override {
371 if (!IsContinuous()) {
372 LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
373 << " XPRESS_MIXED_INTEGER_PROGRAMMING";
374 return 0.0;
375 }
376
377 // TODO(user): Not yet working.
378 LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
379 << " XPRESS_LINEAR_PROGRAMMING";
380 return 0.0;
381 }
382
383 void SetCallback(MPCallback* mp_callback) override;
384 bool SupportsCallbacks() const override { return true; }
385
386 bool InterruptSolve() override {
387 if (mLp) XPRSinterrupt(mLp, XPRS_STOP_USER);
388 return true;
389 }
390
391 protected:
392 // Set all parameters in the underlying solver.
393 void SetParameters(MPSolverParameters const& param) override;
394 // Set each parameter in the underlying solver.
395 void SetRelativeMipGap(double value) override;
396 void SetPrimalTolerance(double value) override;
397 void SetDualTolerance(double value) override;
398 void SetPresolveMode(int value) override;
399 void SetScalingMode(int value) override;
400 void SetLpAlgorithm(int value) override;
401
402 virtual bool ReadParameterFile(std::string const& filename);
403 virtual std::string ValidFileExtensionForParameterFile() const;
404
405 private:
406 // Mark modeling object "out of sync". This implicitly invalidates
407 // solution information as well. It is the counterpart of
408 // MPSolverInterface::InvalidateSolutionSynchronization
409 void InvalidateModelSynchronization() {
410 mCstat.clear();
411 mRstat.clear();
413 }
414 // Adds a new feasible, infeasible or partial MIP solution for the problem to
415 // the Optimizer. The hint is read in the MPSolver where the user set it using
416 // SetHint()
417 void AddSolutionHintToOptimizer();
418
419 bool readParameters(std::istream& is, char sep);
420
421 private:
422 XPRSprob mLp;
423 bool const mMip;
424 // Incremental extraction.
425 // Without incremental extraction we have to re-extract the model every
426 // time we perform a solve. Due to the way the Reset() function is
427 // implemented, this will lose MIP start or basis information from a
428 // previous solve. On the other hand, if there is a significant changes
429 // to the model then just re-extracting everything is usually faster than
430 // keeping the low-level modeling object in sync with the high-level
431 // variables/constraints.
432 // Note that incremental extraction is particularly expensive in function
433 // ExtractNewVariables() since there we must scan _all_ old constraints
434 // and update them with respect to the new variables.
435 bool const supportIncrementalExtraction;
436
437 // Use slow and immediate updates or try to do bulk updates.
438 // For many updates to the model we have the option to either perform
439 // the update immediately with a potentially slow operation or to
440 // just mark the low-level modeling object out of sync and re-extract
441 // the model later.
442 enum SlowUpdates {
443 SlowSetCoefficient = 0x0001,
444 SlowClearConstraint = 0x0002,
445 SlowSetObjectiveCoefficient = 0x0004,
446 SlowClearObjective = 0x0008,
447 SlowSetConstraintBounds = 0x0010,
448 SlowSetVariableInteger = 0x0020,
449 SlowSetVariableBounds = 0x0040,
450 SlowUpdatesAll = 0xffff
451 } const slowUpdates;
452 // XPRESS has no method to query the basis status of a single variable.
453 // Hence, we query the status only once and cache the array. This is
454 // much faster in case the basis status of more than one row/column
455 // is required.
456
457 // TODO
458 std::vector<int> mutable mCstat;
459 std::vector<int> mutable mRstat;
460
461 std::vector<int> mutable initial_variables_basis_status_;
462 std::vector<int> mutable initial_constraint_basis_status_;
463
464 // Setup the right-hand side of a constraint from its lower and upper bound.
465 static void MakeRhs(double lb, double ub, double& rhs, char& sense,
466 double& range);
467
468 std::map<std::string, int>& mapStringControls_;
469 std::map<std::string, int>& mapDoubleControls_;
470 std::map<std::string, int>& mapIntegerControls_;
471 std::map<std::string, int>& mapInteger64Controls_;
472
473 bool SetSolverSpecificParametersAsString(
474 const std::string& parameters) override;
475 MPCallback* callback_ = nullptr;
476};
477
478// Transform MPSolver basis status to XPRESS status
480 MPSolver::BasisStatus mpsolver_basis_status);
481// Transform XPRESS basis status to MPSolver basis status.
483 int xpress_basis_status);
484
485static std::map<std::string, int>& getMapStringControls() {
486 static std::map<std::string, int> mapControls = {
487 {"MPSRHSNAME", XPRS_MPSRHSNAME},
488 {"MPSOBJNAME", XPRS_MPSOBJNAME},
489 {"MPSRANGENAME", XPRS_MPSRANGENAME},
490 {"MPSBOUNDNAME", XPRS_MPSBOUNDNAME},
491 {"OUTPUTMASK", XPRS_OUTPUTMASK},
492 {"TUNERMETHODFILE", XPRS_TUNERMETHODFILE},
493 {"TUNEROUTPUTPATH", XPRS_TUNEROUTPUTPATH},
494 {"TUNERSESSIONNAME", XPRS_TUNERSESSIONNAME},
495 {"COMPUTEEXECSERVICE", XPRS_COMPUTEEXECSERVICE},
496 };
497 return mapControls;
498}
499
500static std::map<std::string, int>& getMapDoubleControls() {
501 static std::map<std::string, int> mapControls = {
502 {"MAXCUTTIME", XPRS_MAXCUTTIME},
503 {"MAXSTALLTIME", XPRS_MAXSTALLTIME},
504 {"TUNERMAXTIME", XPRS_TUNERMAXTIME},
505 {"MATRIXTOL", XPRS_MATRIXTOL},
506 {"PIVOTTOL", XPRS_PIVOTTOL},
507 {"FEASTOL", XPRS_FEASTOL},
508 {"OUTPUTTOL", XPRS_OUTPUTTOL},
509 {"SOSREFTOL", XPRS_SOSREFTOL},
510 {"OPTIMALITYTOL", XPRS_OPTIMALITYTOL},
511 {"ETATOL", XPRS_ETATOL},
512 {"RELPIVOTTOL", XPRS_RELPIVOTTOL},
513 {"MIPTOL", XPRS_MIPTOL},
514 {"MIPTOLTARGET", XPRS_MIPTOLTARGET},
515 {"BARPERTURB", XPRS_BARPERTURB},
516 {"MIPADDCUTOFF", XPRS_MIPADDCUTOFF},
517 {"MIPABSCUTOFF", XPRS_MIPABSCUTOFF},
518 {"MIPRELCUTOFF", XPRS_MIPRELCUTOFF},
519 {"PSEUDOCOST", XPRS_PSEUDOCOST},
520 {"PENALTY", XPRS_PENALTY},
521 {"BIGM", XPRS_BIGM},
522 {"MIPABSSTOP", XPRS_MIPABSSTOP},
523 {"MIPRELSTOP", XPRS_MIPRELSTOP},
524 {"CROSSOVERACCURACYTOL", XPRS_CROSSOVERACCURACYTOL},
525 {"PRIMALPERTURB", XPRS_PRIMALPERTURB},
526 {"DUALPERTURB", XPRS_DUALPERTURB},
527 {"BAROBJSCALE", XPRS_BAROBJSCALE},
528 {"BARRHSSCALE", XPRS_BARRHSSCALE},
529 {"CHOLESKYTOL", XPRS_CHOLESKYTOL},
530 {"BARGAPSTOP", XPRS_BARGAPSTOP},
531 {"BARDUALSTOP", XPRS_BARDUALSTOP},
532 {"BARPRIMALSTOP", XPRS_BARPRIMALSTOP},
533 {"BARSTEPSTOP", XPRS_BARSTEPSTOP},
534 {"ELIMTOL", XPRS_ELIMTOL},
535 {"MARKOWITZTOL", XPRS_MARKOWITZTOL},
536 {"MIPABSGAPNOTIFY", XPRS_MIPABSGAPNOTIFY},
537 {"MIPRELGAPNOTIFY", XPRS_MIPRELGAPNOTIFY},
538 {"BARLARGEBOUND", XPRS_BARLARGEBOUND},
539 {"PPFACTOR", XPRS_PPFACTOR},
540 {"REPAIRINDEFINITEQMAX", XPRS_REPAIRINDEFINITEQMAX},
541 {"BARGAPTARGET", XPRS_BARGAPTARGET},
542 {"DUMMYCONTROL", XPRS_DUMMYCONTROL},
543 {"BARSTARTWEIGHT", XPRS_BARSTARTWEIGHT},
544 {"BARFREESCALE", XPRS_BARFREESCALE},
545 {"SBEFFORT", XPRS_SBEFFORT},
546 {"HEURDIVERANDOMIZE", XPRS_HEURDIVERANDOMIZE},
547 {"HEURSEARCHEFFORT", XPRS_HEURSEARCHEFFORT},
548 {"CUTFACTOR", XPRS_CUTFACTOR},
549 {"EIGENVALUETOL", XPRS_EIGENVALUETOL},
550 {"INDLINBIGM", XPRS_INDLINBIGM},
551 {"TREEMEMORYSAVINGTARGET", XPRS_TREEMEMORYSAVINGTARGET},
552 {"INDPRELINBIGM", XPRS_INDPRELINBIGM},
553 {"RELAXTREEMEMORYLIMIT", XPRS_RELAXTREEMEMORYLIMIT},
554 {"MIPABSGAPNOTIFYOBJ", XPRS_MIPABSGAPNOTIFYOBJ},
555 {"MIPABSGAPNOTIFYBOUND", XPRS_MIPABSGAPNOTIFYBOUND},
556 {"PRESOLVEMAXGROW", XPRS_PRESOLVEMAXGROW},
557 {"HEURSEARCHTARGETSIZE", XPRS_HEURSEARCHTARGETSIZE},
558 {"CROSSOVERRELPIVOTTOL", XPRS_CROSSOVERRELPIVOTTOL},
559 {"CROSSOVERRELPIVOTTOLSAFE", XPRS_CROSSOVERRELPIVOTTOLSAFE},
560 {"DETLOGFREQ", XPRS_DETLOGFREQ},
561 {"MAXIMPLIEDBOUND", XPRS_MAXIMPLIEDBOUND},
562 {"FEASTOLTARGET", XPRS_FEASTOLTARGET},
563 {"OPTIMALITYTOLTARGET", XPRS_OPTIMALITYTOLTARGET},
564 {"PRECOMPONENTSEFFORT", XPRS_PRECOMPONENTSEFFORT},
565 {"LPLOGDELAY", XPRS_LPLOGDELAY},
566 {"HEURDIVEITERLIMIT", XPRS_HEURDIVEITERLIMIT},
567 {"BARKERNEL", XPRS_BARKERNEL},
568 {"FEASTOLPERTURB", XPRS_FEASTOLPERTURB},
569 {"CROSSOVERFEASWEIGHT", XPRS_CROSSOVERFEASWEIGHT},
570 {"LUPIVOTTOL", XPRS_LUPIVOTTOL},
571 {"MIPRESTARTGAPTHRESHOLD", XPRS_MIPRESTARTGAPTHRESHOLD},
572 {"NODEPROBINGEFFORT", XPRS_NODEPROBINGEFFORT},
573 {"INPUTTOL", XPRS_INPUTTOL},
574 {"MIPRESTARTFACTOR", XPRS_MIPRESTARTFACTOR},
575 {"BAROBJPERTURB", XPRS_BAROBJPERTURB},
576 {"CPIALPHA", XPRS_CPIALPHA},
577 {"GLOBALBOUNDINGBOX", XPRS_GLOBALBOUNDINGBOX},
578 {"TIMELIMIT", XPRS_TIMELIMIT},
579 {"SOLTIMELIMIT", XPRS_SOLTIMELIMIT},
580 {"REPAIRINFEASTIMELIMIT", XPRS_REPAIRINFEASTIMELIMIT},
581 };
582 return mapControls;
583}
584
585static std::map<std::string, int>& getMapIntControls() {
586 static std::map<std::string, int> mapControls = {
587 {"EXTRAROWS", XPRS_EXTRAROWS},
588 {"EXTRACOLS", XPRS_EXTRACOLS},
589 {"LPITERLIMIT", XPRS_LPITERLIMIT},
590 {"LPLOG", XPRS_LPLOG},
591 {"SCALING", XPRS_SCALING},
592 {"PRESOLVE", XPRS_PRESOLVE},
593 {"CRASH", XPRS_CRASH},
594 {"PRICINGALG", XPRS_PRICINGALG},
595 {"INVERTFREQ", XPRS_INVERTFREQ},
596 {"INVERTMIN", XPRS_INVERTMIN},
597 {"MAXNODE", XPRS_MAXNODE},
598 {"MAXTIME", XPRS_MAXTIME},
599 {"MAXMIPSOL", XPRS_MAXMIPSOL},
600 {"SIFTPASSES", XPRS_SIFTPASSES},
601 {"DEFAULTALG", XPRS_DEFAULTALG},
602 {"VARSELECTION", XPRS_VARSELECTION},
603 {"NODESELECTION", XPRS_NODESELECTION},
604 {"BACKTRACK", XPRS_BACKTRACK},
605 {"MIPLOG", XPRS_MIPLOG},
606 {"KEEPNROWS", XPRS_KEEPNROWS},
607 {"MPSECHO", XPRS_MPSECHO},
608 {"MAXPAGELINES", XPRS_MAXPAGELINES},
609 {"OUTPUTLOG", XPRS_OUTPUTLOG},
610 {"BARSOLUTION", XPRS_BARSOLUTION},
611 {"CACHESIZE", XPRS_CACHESIZE},
612 {"CROSSOVER", XPRS_CROSSOVER},
613 {"BARITERLIMIT", XPRS_BARITERLIMIT},
614 {"CHOLESKYALG", XPRS_CHOLESKYALG},
615 {"BAROUTPUT", XPRS_BAROUTPUT},
616 {"EXTRAMIPENTS", XPRS_EXTRAMIPENTS},
617 {"REFACTOR", XPRS_REFACTOR},
618 {"BARTHREADS", XPRS_BARTHREADS},
619 {"KEEPBASIS", XPRS_KEEPBASIS},
620 {"CROSSOVEROPS", XPRS_CROSSOVEROPS},
621 {"VERSION", XPRS_VERSION},
622 {"CROSSOVERTHREADS", XPRS_CROSSOVERTHREADS},
623 {"BIGMMETHOD", XPRS_BIGMMETHOD},
624 {"MPSNAMELENGTH", XPRS_MPSNAMELENGTH},
625 {"ELIMFILLIN", XPRS_ELIMFILLIN},
626 {"PRESOLVEOPS", XPRS_PRESOLVEOPS},
627 {"MIPPRESOLVE", XPRS_MIPPRESOLVE},
628 {"MIPTHREADS", XPRS_MIPTHREADS},
629 {"BARORDER", XPRS_BARORDER},
630 {"BREADTHFIRST", XPRS_BREADTHFIRST},
631 {"AUTOPERTURB", XPRS_AUTOPERTURB},
632 {"DENSECOLLIMIT", XPRS_DENSECOLLIMIT},
633 {"CALLBACKFROMMASTERTHREAD", XPRS_CALLBACKFROMMASTERTHREAD},
634 {"MAXMCOEFFBUFFERELEMS", XPRS_MAXMCOEFFBUFFERELEMS},
635 {"REFINEOPS", XPRS_REFINEOPS},
636 {"LPREFINEITERLIMIT", XPRS_LPREFINEITERLIMIT},
637 {"MIPREFINEITERLIMIT", XPRS_MIPREFINEITERLIMIT},
638 {"DUALIZEOPS", XPRS_DUALIZEOPS},
639 {"CROSSOVERITERLIMIT", XPRS_CROSSOVERITERLIMIT},
640 {"PREBASISRED", XPRS_PREBASISRED},
641 {"PRESORT", XPRS_PRESORT},
642 {"PREPERMUTE", XPRS_PREPERMUTE},
643 {"PREPERMUTESEED", XPRS_PREPERMUTESEED},
644 {"MAXMEMORYSOFT", XPRS_MAXMEMORYSOFT},
645 {"CUTFREQ", XPRS_CUTFREQ},
646 {"SYMSELECT", XPRS_SYMSELECT},
647 {"SYMMETRY", XPRS_SYMMETRY},
648 {"MAXMEMORYHARD", XPRS_MAXMEMORYHARD},
649 {"MIQCPALG", XPRS_MIQCPALG},
650 {"QCCUTS", XPRS_QCCUTS},
651 {"QCROOTALG", XPRS_QCROOTALG},
652 {"PRECONVERTSEPARABLE", XPRS_PRECONVERTSEPARABLE},
653 {"ALGAFTERNETWORK", XPRS_ALGAFTERNETWORK},
654 {"TRACE", XPRS_TRACE},
655 {"MAXIIS", XPRS_MAXIIS},
656 {"CPUTIME", XPRS_CPUTIME},
657 {"COVERCUTS", XPRS_COVERCUTS},
658 {"GOMCUTS", XPRS_GOMCUTS},
659 {"LPFOLDING", XPRS_LPFOLDING},
660 {"MPSFORMAT", XPRS_MPSFORMAT},
661 {"CUTSTRATEGY", XPRS_CUTSTRATEGY},
662 {"CUTDEPTH", XPRS_CUTDEPTH},
663 {"TREECOVERCUTS", XPRS_TREECOVERCUTS},
664 {"TREEGOMCUTS", XPRS_TREEGOMCUTS},
665 {"CUTSELECT", XPRS_CUTSELECT},
666 {"TREECUTSELECT", XPRS_TREECUTSELECT},
667 {"DUALIZE", XPRS_DUALIZE},
668 {"DUALGRADIENT", XPRS_DUALGRADIENT},
669 {"SBITERLIMIT", XPRS_SBITERLIMIT},
670 {"SBBEST", XPRS_SBBEST},
671 {"BARINDEFLIMIT", XPRS_BARINDEFLIMIT},
672 {"HEURFREQ", XPRS_HEURFREQ},
673 {"HEURDEPTH", XPRS_HEURDEPTH},
674 {"HEURMAXSOL", XPRS_HEURMAXSOL},
675 {"HEURNODES", XPRS_HEURNODES},
676 {"LNPBEST", XPRS_LNPBEST},
677 {"LNPITERLIMIT", XPRS_LNPITERLIMIT},
678 {"BRANCHCHOICE", XPRS_BRANCHCHOICE},
679 {"BARREGULARIZE", XPRS_BARREGULARIZE},
680 {"SBSELECT", XPRS_SBSELECT},
681 {"LOCALCHOICE", XPRS_LOCALCHOICE},
682 {"LOCALBACKTRACK", XPRS_LOCALBACKTRACK},
683 {"DUALSTRATEGY", XPRS_DUALSTRATEGY},
684 {"L1CACHE", XPRS_L1CACHE},
685 {"HEURDIVESTRATEGY", XPRS_HEURDIVESTRATEGY},
686 {"HEURSELECT", XPRS_HEURSELECT},
687 {"BARSTART", XPRS_BARSTART},
688 {"PRESOLVEPASSES", XPRS_PRESOLVEPASSES},
689 {"BARNUMSTABILITY", XPRS_BARNUMSTABILITY},
690 {"BARORDERTHREADS", XPRS_BARORDERTHREADS},
691 {"EXTRASETS", XPRS_EXTRASETS},
692 {"FEASIBILITYPUMP", XPRS_FEASIBILITYPUMP},
693 {"PRECOEFELIM", XPRS_PRECOEFELIM},
694 {"PREDOMCOL", XPRS_PREDOMCOL},
695 {"HEURSEARCHFREQ", XPRS_HEURSEARCHFREQ},
696 {"HEURDIVESPEEDUP", XPRS_HEURDIVESPEEDUP},
697 {"SBESTIMATE", XPRS_SBESTIMATE},
698 {"BARCORES", XPRS_BARCORES},
699 {"MAXCHECKSONMAXTIME", XPRS_MAXCHECKSONMAXTIME},
700 {"MAXCHECKSONMAXCUTTIME", XPRS_MAXCHECKSONMAXCUTTIME},
701 {"HISTORYCOSTS", XPRS_HISTORYCOSTS},
702 {"ALGAFTERCROSSOVER", XPRS_ALGAFTERCROSSOVER},
703 {"MUTEXCALLBACKS", XPRS_MUTEXCALLBACKS},
704 {"BARCRASH", XPRS_BARCRASH},
705 {"HEURDIVESOFTROUNDING", XPRS_HEURDIVESOFTROUNDING},
706 {"HEURSEARCHROOTSELECT", XPRS_HEURSEARCHROOTSELECT},
707 {"HEURSEARCHTREESELECT", XPRS_HEURSEARCHTREESELECT},
708 {"MPS18COMPATIBLE", XPRS_MPS18COMPATIBLE},
709 {"ROOTPRESOLVE", XPRS_ROOTPRESOLVE},
710 {"CROSSOVERDRP", XPRS_CROSSOVERDRP},
711 {"FORCEOUTPUT", XPRS_FORCEOUTPUT},
712 {"PRIMALOPS", XPRS_PRIMALOPS},
713 {"DETERMINISTIC", XPRS_DETERMINISTIC},
714 {"PREPROBING", XPRS_PREPROBING},
715 {"TREEMEMORYLIMIT", XPRS_TREEMEMORYLIMIT},
716 {"TREECOMPRESSION", XPRS_TREECOMPRESSION},
717 {"TREEDIAGNOSTICS", XPRS_TREEDIAGNOSTICS},
718 {"MAXTREEFILESIZE", XPRS_MAXTREEFILESIZE},
719 {"PRECLIQUESTRATEGY", XPRS_PRECLIQUESTRATEGY},
720 {"REPAIRINFEASMAXTIME", XPRS_REPAIRINFEASMAXTIME},
721 {"IFCHECKCONVEXITY", XPRS_IFCHECKCONVEXITY},
722 {"PRIMALUNSHIFT", XPRS_PRIMALUNSHIFT},
723 {"REPAIRINDEFINITEQ", XPRS_REPAIRINDEFINITEQ},
724 {"MIPRAMPUP", XPRS_MIPRAMPUP},
725 {"MAXLOCALBACKTRACK", XPRS_MAXLOCALBACKTRACK},
726 {"USERSOLHEURISTIC", XPRS_USERSOLHEURISTIC},
727 {"FORCEPARALLELDUAL", XPRS_FORCEPARALLELDUAL},
728 {"BACKTRACKTIE", XPRS_BACKTRACKTIE},
729 {"BRANCHDISJ", XPRS_BRANCHDISJ},
730 {"MIPFRACREDUCE", XPRS_MIPFRACREDUCE},
731 {"CONCURRENTTHREADS", XPRS_CONCURRENTTHREADS},
732 {"MAXSCALEFACTOR", XPRS_MAXSCALEFACTOR},
733 {"HEURTHREADS", XPRS_HEURTHREADS},
734 {"THREADS", XPRS_THREADS},
735 {"HEURBEFORELP", XPRS_HEURBEFORELP},
736 {"PREDOMROW", XPRS_PREDOMROW},
737 {"BRANCHSTRUCTURAL", XPRS_BRANCHSTRUCTURAL},
738 {"QUADRATICUNSHIFT", XPRS_QUADRATICUNSHIFT},
739 {"BARPRESOLVEOPS", XPRS_BARPRESOLVEOPS},
740 {"QSIMPLEXOPS", XPRS_QSIMPLEXOPS},
741 {"MIPRESTART", XPRS_MIPRESTART},
742 {"CONFLICTCUTS", XPRS_CONFLICTCUTS},
743 {"PREPROTECTDUAL", XPRS_PREPROTECTDUAL},
744 {"CORESPERCPU", XPRS_CORESPERCPU},
745 {"RESOURCESTRATEGY", XPRS_RESOURCESTRATEGY},
746 {"CLAMPING", XPRS_CLAMPING},
747 {"SLEEPONTHREADWAIT", XPRS_SLEEPONTHREADWAIT},
748 {"PREDUPROW", XPRS_PREDUPROW},
749 {"CPUPLATFORM", XPRS_CPUPLATFORM},
750 {"BARALG", XPRS_BARALG},
751 {"SIFTING", XPRS_SIFTING},
752 {"LPLOGSTYLE", XPRS_LPLOGSTYLE},
753 {"RANDOMSEED", XPRS_RANDOMSEED},
754 {"TREEQCCUTS", XPRS_TREEQCCUTS},
755 {"PRELINDEP", XPRS_PRELINDEP},
756 {"DUALTHREADS", XPRS_DUALTHREADS},
757 {"PREOBJCUTDETECT", XPRS_PREOBJCUTDETECT},
758 {"PREBNDREDQUAD", XPRS_PREBNDREDQUAD},
759 {"PREBNDREDCONE", XPRS_PREBNDREDCONE},
760 {"PRECOMPONENTS", XPRS_PRECOMPONENTS},
761 {"MAXMIPTASKS", XPRS_MAXMIPTASKS},
762 {"MIPTERMINATIONMETHOD", XPRS_MIPTERMINATIONMETHOD},
763 {"PRECONEDECOMP", XPRS_PRECONEDECOMP},
764 {"HEURFORCESPECIALOBJ", XPRS_HEURFORCESPECIALOBJ},
765 {"HEURSEARCHROOTCUTFREQ", XPRS_HEURSEARCHROOTCUTFREQ},
766 {"PREELIMQUAD", XPRS_PREELIMQUAD},
767 {"PREIMPLICATIONS", XPRS_PREIMPLICATIONS},
768 {"TUNERMODE", XPRS_TUNERMODE},
769 {"TUNERMETHOD", XPRS_TUNERMETHOD},
770 {"TUNERTARGET", XPRS_TUNERTARGET},
771 {"TUNERTHREADS", XPRS_TUNERTHREADS},
772 {"TUNERHISTORY", XPRS_TUNERHISTORY},
773 {"TUNERPERMUTE", XPRS_TUNERPERMUTE},
774 {"TUNERVERBOSE", XPRS_TUNERVERBOSE},
775 {"TUNEROUTPUT", XPRS_TUNEROUTPUT},
776 {"PREANALYTICCENTER", XPRS_PREANALYTICCENTER},
777 {"NETCUTS", XPRS_NETCUTS},
778 {"LPFLAGS", XPRS_LPFLAGS},
779 {"MIPKAPPAFREQ", XPRS_MIPKAPPAFREQ},
780 {"OBJSCALEFACTOR", XPRS_OBJSCALEFACTOR},
781 {"TREEFILELOGINTERVAL", XPRS_TREEFILELOGINTERVAL},
782 {"IGNORECONTAINERCPULIMIT", XPRS_IGNORECONTAINERCPULIMIT},
783 {"IGNORECONTAINERMEMORYLIMIT", XPRS_IGNORECONTAINERMEMORYLIMIT},
784 {"MIPDUALREDUCTIONS", XPRS_MIPDUALREDUCTIONS},
785 {"GENCONSDUALREDUCTIONS", XPRS_GENCONSDUALREDUCTIONS},
786 {"PWLDUALREDUCTIONS", XPRS_PWLDUALREDUCTIONS},
787 {"BARFAILITERLIMIT", XPRS_BARFAILITERLIMIT},
788 {"AUTOSCALING", XPRS_AUTOSCALING},
789 {"GENCONSABSTRANSFORMATION", XPRS_GENCONSABSTRANSFORMATION},
790 {"COMPUTEJOBPRIORITY", XPRS_COMPUTEJOBPRIORITY},
791 {"PREFOLDING", XPRS_PREFOLDING},
792 {"NETSTALLLIMIT", XPRS_NETSTALLLIMIT},
793 {"SERIALIZEPREINTSOL", XPRS_SERIALIZEPREINTSOL},
794 {"NUMERICALEMPHASIS", XPRS_NUMERICALEMPHASIS},
795 {"PWLNONCONVEXTRANSFORMATION", XPRS_PWLNONCONVEXTRANSFORMATION},
796 {"MIPCOMPONENTS", XPRS_MIPCOMPONENTS},
797 {"MIPCONCURRENTNODES", XPRS_MIPCONCURRENTNODES},
798 {"MIPCONCURRENTSOLVES", XPRS_MIPCONCURRENTSOLVES},
799 {"OUTPUTCONTROLS", XPRS_OUTPUTCONTROLS},
800 {"SIFTSWITCH", XPRS_SIFTSWITCH},
801 {"HEUREMPHASIS", XPRS_HEUREMPHASIS},
802 {"COMPUTEMATX", XPRS_COMPUTEMATX},
803 {"COMPUTEMATX_IIS", XPRS_COMPUTEMATX_IIS},
804 {"COMPUTEMATX_IISMAXTIME", XPRS_COMPUTEMATX_IISMAXTIME},
805 {"BARREFITER", XPRS_BARREFITER},
806 {"COMPUTELOG", XPRS_COMPUTELOG},
807 {"SIFTPRESOLVEOPS", XPRS_SIFTPRESOLVEOPS},
808 {"CHECKINPUTDATA", XPRS_CHECKINPUTDATA},
809 {"ESCAPENAMES", XPRS_ESCAPENAMES},
810 {"IOTIMEOUT", XPRS_IOTIMEOUT},
811 {"AUTOCUTTING", XPRS_AUTOCUTTING},
812 {"CALLBACKCHECKTIMEDELAY", XPRS_CALLBACKCHECKTIMEDELAY},
813 {"MULTIOBJOPS", XPRS_MULTIOBJOPS},
814 {"MULTIOBJLOG", XPRS_MULTIOBJLOG},
815 {"GLOBALSPATIALBRANCHIFPREFERORIG", XPRS_GLOBALSPATIALBRANCHIFPREFERORIG},
816 {"PRECONFIGURATION", XPRS_PRECONFIGURATION},
817 {"FEASIBILITYJUMP", XPRS_FEASIBILITYJUMP},
818 };
819 return mapControls;
820}
821
822static std::map<std::string, int>& getMapInt64Controls() {
823 static std::map<std::string, int> mapControls = {
824 {"EXTRAELEMS", XPRS_EXTRAELEMS},
825 {"EXTRASETELEMS", XPRS_EXTRASETELEMS},
826 };
827 return mapControls;
828}
829
830// Creates an LP/MIP instance.
832 : MPSolverInterface(solver),
833 mLp(nullptr),
834 mMip(mip),
835 supportIncrementalExtraction(false),
836 slowUpdates(SlowClearObjective),
837 mapStringControls_(getMapStringControls()),
838 mapDoubleControls_(getMapDoubleControls()),
839 mapIntegerControls_(getMapIntControls()),
840 mapInteger64Controls_(getMapInt64Controls()) {
841 bool correctlyLoaded = initXpressEnv();
842 CHECK(correctlyLoaded);
843 int status = XPRScreateprob(&mLp);
844 CHECK_STATUS(status);
845 DCHECK(mLp != nullptr); // should not be NULL if status=0
846 int nReturn = XPRSaddcbmessage(mLp, optimizermsg, (void*)this, 0);
847 CHECK_STATUS(XPRSloadlp(mLp, "newProb", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
850}
851
856
858 // We prefer XPRSversionnumber() over XPRSversion() since the
859 // former will never pose any encoding issues.
860 int version = 0;
862
863 int const major = version / 1000000;
864 version -= major * 1000000;
865 int const release = version / 10000;
866 version -= release * 10000;
867 int const mod = version / 100;
868 version -= mod * 100;
869 int const fix = version;
870
871 return absl::StrFormat("XPRESS library version %d.%02d.%02d.%02d", major,
872 release, mod, fix);
873}
874
875// ------ Model modifications and extraction -----
876
878 // Instead of explicitly clearing all modeling objects we
879 // just delete the problem object and allocate a new one.
881
882 int status;
883 status = XPRScreateprob(&mLp);
884 CHECK_STATUS(status);
885 DCHECK(mLp != nullptr); // should not be NULL if status=0
886 int nReturn = XPRSaddcbmessage(mLp, optimizermsg, (void*)this, 0);
887 CHECK_STATUS(XPRSloadlp(mLp, "newProb", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
888
891
893 mCstat.clear();
894 mRstat.clear();
895}
896
901
902void XpressInterface::SetVariableBounds(int var_index, double lb, double ub) {
904
905 // Changing the bounds of a variable is fast. However, doing this for
906 // many variables may still be slow. So we don't perform the update by
907 // default. However, if we support incremental extraction
908 // (supportIncrementalExtraction is true) then we MUST perform the
909 // update here, or we will lose it.
910
911 if (!supportIncrementalExtraction && !(slowUpdates & SlowSetVariableBounds)) {
912 InvalidateModelSynchronization();
913 } else {
914 if (variable_is_extracted(var_index)) {
915 // Variable has already been extracted, so we must modify the
916 // modeling object.
917 DCHECK_LT(var_index, last_variable_index_);
918 char const lu[2] = {'L', 'U'};
919 double const bd[2] = {lb, ub};
920 int const idx[2] = {var_index, var_index};
921 CHECK_STATUS(XPRSchgbounds(mLp, 2, idx, lu, bd));
922 } else {
923 // Variable is not yet extracted. It is sufficient to just mark
924 // the modeling object "out of sync"
925 InvalidateModelSynchronization();
926 }
927 }
928}
929
930// Modifies integrality of an extracted variable.
931void XpressInterface::SetVariableInteger(int var_index, bool integer) {
933
934 // NOTE: The type of the model (continuous or mixed integer) is
935 // defined once and for all in the constructor. There are no
936 // dynamic changes to the model type.
937
938 // Changing the type of a variable should be fast. Still, doing all
939 // updates in one big chunk right before solve() is usually faster.
940 // However, if we support incremental extraction
941 // (supportIncrementalExtraction is true) then we MUST change the
942 // type of extracted variables here.
943
944 if (!supportIncrementalExtraction &&
945 !(slowUpdates & SlowSetVariableInteger)) {
946 InvalidateModelSynchronization();
947 } else {
948 if (mMip) {
949 if (variable_is_extracted(var_index)) {
950 // Variable is extracted. Change the type immediately.
951 // TODO: Should we check the current type and don't do anything
952 // in case the type does not change?
953 DCHECK_LE(var_index, getnumcols(mLp));
954 char const type = integer ? XPRS_INTEGER : XPRS_CONTINUOUS;
955 CHECK_STATUS(XPRSchgcoltype(mLp, 1, &var_index, &type));
956 } else {
957 InvalidateModelSynchronization();
958 }
959 } else {
960 LOG(DFATAL)
961 << "Attempt to change variable to integer in non-MIP problem!";
962 }
963 }
964}
965
966// Setup the right-hand side of a constraint.
967void XpressInterface::MakeRhs(double lb, double ub, double& rhs, char& sense,
968 double& range) {
969 if (lb == ub) {
970 // Both bounds are equal -> this is an equality constraint
971 rhs = lb;
972 range = 0.0;
973 sense = 'E';
974 } else if (lb > XPRS_MINUSINFINITY && ub < XPRS_PLUSINFINITY) {
975 // Both bounds are finite -> this is a ranged constraint
976 // The value of a ranged constraint is allowed to be in
977 // [ rhs-rngval, rhs ]
978 // Xpress does not support contradictory bounds. Instead the sign on
979 // rndval is always ignored.
980 if (lb > ub) {
981 // TODO check if this is ok for the user
982 LOG(DFATAL) << "XPRESS does not support contradictory bounds on range "
983 "constraints! ["
984 << lb << ", " << ub << "] will be converted to " << ub << ", "
985 << (ub - std::abs(ub - lb)) << "]";
986 }
987 rhs = ub;
988 range = std::abs(
989 ub - lb); // This happens implicitly by XPRSaddrows() and XPRSloadlp()
990 sense = 'R';
991 } else if (ub < XPRS_PLUSINFINITY || (std::abs(ub) == XPRS_PLUSINFINITY &&
992 std::abs(lb) > XPRS_PLUSINFINITY)) {
993 // Finite upper, infinite lower bound -> this is a <= constraint
994 rhs = ub;
995 range = 0.0;
996 sense = 'L';
997 } else if (lb > XPRS_MINUSINFINITY || (std::abs(lb) == XPRS_PLUSINFINITY &&
998 std::abs(ub) > XPRS_PLUSINFINITY)) {
999 // Finite lower, infinite upper bound -> this is a >= constraint
1000 rhs = lb;
1001 range = 0.0;
1002 sense = 'G';
1003 } else {
1004 // Lower and upper bound are both infinite.
1005 // This is used for example in .mps files to specify alternate
1006 // objective functions.
1007 // A free row is denoted by sense 'N' and we can specify arbitrary
1008 // right-hand sides since they are ignored anyway. We just pick the
1009 // bound with smaller absolute value.
1010 DCHECK_GE(std::abs(lb), XPRS_PLUSINFINITY);
1011 DCHECK_GE(std::abs(ub), XPRS_PLUSINFINITY);
1012 if (std::abs(lb) < std::abs(ub))
1013 rhs = lb;
1014 else
1015 rhs = ub;
1016 range = 0.0;
1017 sense = 'N';
1018 }
1019}
1020
1021void XpressInterface::SetConstraintBounds(int index, double lb, double ub) {
1023
1024 // Changing rhs, sense, or range of a constraint is not too slow.
1025 // Still, doing all the updates in one large operation is faster.
1026 // Note however that if we do not want to re-extract the full model
1027 // for each solve (supportIncrementalExtraction is true) then we MUST
1028 // update the constraint here, otherwise we lose this update information.
1029
1030 if (!supportIncrementalExtraction &&
1031 !(slowUpdates & SlowSetConstraintBounds)) {
1032 InvalidateModelSynchronization();
1033 } else {
1034 if (constraint_is_extracted(index)) {
1035 // Constraint is already extracted, so we must update its bounds
1036 // and its type.
1037 DCHECK(mLp != nullptr);
1038 char sense;
1039 double range, rhs;
1040 MakeRhs(lb, ub, rhs, sense, range);
1041 if (sense == 'R') {
1042 // Rather than doing the complicated analysis required for
1043 // XPRSchgrhsrange(), we first convert the row into an 'L' row
1044 // with defined rhs and then change the range value.
1045 CHECK_STATUS(XPRSchgrowtype(mLp, 1, &index, "L"));
1046 CHECK_STATUS(XPRSchgrhs(mLp, 1, &index, &rhs));
1047 CHECK_STATUS(XPRSchgrhsrange(mLp, 1, &index, &range));
1048 } else {
1049 CHECK_STATUS(XPRSchgrowtype(mLp, 1, &index, &sense));
1050 CHECK_STATUS(XPRSchgrhs(mLp, 1, &index, &rhs));
1051 }
1052 } else {
1053 // Constraint is not yet extracted. It is sufficient to mark the
1054 // modeling object as "out of sync"
1055 InvalidateModelSynchronization();
1056 }
1057 }
1058}
1059
1061 // This is currently only invoked when a new constraint is created,
1062 // see MPSolver::MakeRowConstraint().
1063 // At this point we only have the lower and upper bounds of the
1064 // constraint. We could immediately call XPRSaddrows() here but it is
1065 // usually much faster to handle the fully populated constraint in
1066 // ExtractNewConstraints() right before the solve.
1067
1068 // TODO
1069 // Make new constraints basic (rowstat[jrow]=1)
1070 // Try not to delete basic variables, or non-basic constraints.
1071 InvalidateModelSynchronization();
1072}
1073
1075 // This is currently only invoked when a new variable is created,
1076 // see MPSolver::MakeVar().
1077 // At this point the variable does not appear in any constraints or
1078 // the objective function. We could invoke XPRSaddcols() to immediately
1079 // create the variable here, but it is usually much faster to handle the
1080 // fully set-up variable in ExtractNewVariables() right before the solve.
1081
1082 // TODO
1083 // Make new variables non-basic at their lower bound (colstat[icol]=0), unless
1084 // a variable has an infinite lower bound and a finite upper bound, in which
1085 // case make the variable non-basic at its upper bound (colstat[icol]=2) Try
1086 // not to delete basic variables, or non-basic constraints.
1087 InvalidateModelSynchronization();
1088}
1089
1091 MPVariable const* const variable,
1092 double new_value, double) {
1094
1095 // Changing a single coefficient in the matrix is potentially pretty
1096 // slow since that coefficient has to be found in the sparse matrix
1097 // representation. So by default we don't perform this update immediately
1098 // but instead mark the low-level modeling object "out of sync".
1099 // If we want to support incremental extraction then we MUST perform
1100 // the modification immediately, or we will lose it.
1101
1102 if (!supportIncrementalExtraction && !(slowUpdates & SlowSetCoefficient)) {
1103 InvalidateModelSynchronization();
1104 } else {
1105 int const row = constraint->index();
1106 int const col = variable->index();
1108 // If row and column are both extracted then we can directly
1109 // update the modeling object
1110 DCHECK_LE(row, last_constraint_index_);
1111 DCHECK_LE(col, last_variable_index_);
1112 CHECK_STATUS(XPRSchgcoef(mLp, row, col, new_value));
1113 } else {
1114 // If either row or column is not yet extracted then we can
1115 // defer the update to ExtractModel()
1116 InvalidateModelSynchronization();
1117 }
1118 }
1119}
1120
1122 int const row = constraint->index();
1123 if (!constraint_is_extracted(row))
1124 // There is nothing to do if the constraint was not even extracted.
1125 return;
1126
1127 // Clearing a constraint means setting all coefficients in the corresponding
1128 // row to 0 (we cannot just delete the row since that would renumber all
1129 // the constraints/rows after it).
1130 // Modifying coefficients in the matrix is potentially pretty expensive
1131 // since they must be found in the sparse matrix representation. That is
1132 // why by default we do not modify the coefficients here but only mark
1133 // the low-level modeling object "out of sync".
1134
1135 if (!(slowUpdates & SlowClearConstraint)) {
1136 InvalidateModelSynchronization();
1137 } else {
1139
1140 int const len = constraint->coefficients_.size();
1141 unique_ptr<int[]> rowind(new int[len]);
1142 unique_ptr<int[]> colind(new int[len]);
1143 unique_ptr<double[]> val(new double[len]);
1144 int j = 0;
1145 const auto& coeffs = constraint->coefficients_;
1146 for (auto coeff : coeffs) {
1147 int const col = coeff.first->index();
1148 if (variable_is_extracted(col)) {
1149 rowind[j] = row;
1150 colind[j] = col;
1151 val[j] = 0.0;
1152 ++j;
1153 }
1154 }
1155 if (j)
1156 CHECK_STATUS(XPRSchgmcoef(mLp, j, rowind.get(), colind.get(), val.get()));
1157 }
1158}
1159
1161 double coefficient) {
1162 int const col = variable->index();
1163 if (!variable_is_extracted(col))
1164 // Nothing to do if variable was not even extracted
1165 return;
1166
1168
1169 // The objective function is stored as a dense vector, so updating a
1170 // single coefficient is O(1). So by default we update the low-level
1171 // modeling object here.
1172 // If we support incremental extraction then we have no choice but to
1173 // perform the update immediately.
1174
1175 if (supportIncrementalExtraction ||
1176 (slowUpdates & SlowSetObjectiveCoefficient)) {
1177 CHECK_STATUS(XPRSchgobj(mLp, 1, &col, &coefficient));
1178 } else {
1179 InvalidateModelSynchronization();
1180 }
1181}
1182
1184 // Changing the objective offset is O(1), so we always do it immediately.
1186 CHECK_STATUS(setobjoffset(mLp, value));
1187}
1188
1191
1192 // Since the objective function is stored as a dense vector updating
1193 // it is O(n), so we usually perform the update immediately.
1194 // If we want to support incremental extraction then we have no choice
1195 // but to perform the update immediately.
1196
1197 if (supportIncrementalExtraction || (slowUpdates & SlowClearObjective)) {
1198 int const cols = getnumcols(mLp);
1199 unique_ptr<int[]> ind(new int[cols]);
1200 unique_ptr<double[]> zero(new double[cols]);
1201 int j = 0;
1202 const auto& coeffs = solver_->objective_->coefficients_;
1203 for (auto coeff : coeffs) {
1204 int const idx = coeff.first->index();
1205 // We only need to reset variables that have been extracted.
1206 if (variable_is_extracted(idx)) {
1207 DCHECK_LT(idx, cols);
1208 ind[j] = idx;
1209 zero[j] = 0.0;
1210 ++j;
1211 }
1212 }
1213 if (j > 0) {
1214 CHECK_STATUS(XPRSchgobj(mLp, j, ind.get(), zero.get()));
1215 }
1216 CHECK_STATUS(setobjoffset(mLp, 0.0));
1217 } else {
1218 InvalidateModelSynchronization();
1219 }
1220}
1221
1222// ------ Query statistics on the solution and the solve ------
1223
1226 return static_cast<int64_t>(getitcnt(mLp));
1227}
1228
1229int64_t XpressInterface::nodes() const {
1230 if (mMip) {
1232 return static_cast<int64_t>(getnodecnt(mLp));
1233 } else {
1234 LOG(DFATAL) << "Number of nodes only available for discrete problems";
1235 return kUnknownNumberOfNodes;
1236 }
1237}
1238
1239// Transform a XPRESS basis status to an MPSolver basis status.
1241 int xpress_basis_status) {
1242 switch (xpress_basis_status) {
1243 case XPRS_AT_LOWER:
1245 case XPRS_BASIC:
1246 return MPSolver::BASIC;
1247 case XPRS_AT_UPPER:
1249 case XPRS_FREE_SUPER:
1250 return MPSolver::FREE;
1251 default:
1252 LOG(DFATAL) << "Unknown XPRESS basis status";
1253 return MPSolver::FREE;
1254 }
1255}
1256
1258 MPSolver::BasisStatus mpsolver_basis_status) {
1259 switch (mpsolver_basis_status) {
1261 return XPRS_AT_LOWER;
1262 case MPSolver::BASIC:
1263 return XPRS_BASIC;
1265 return XPRS_AT_UPPER;
1266 case MPSolver::FREE:
1267 return XPRS_FREE_SUPER;
1269 return XPRS_BASIC;
1270 default:
1271 LOG(DFATAL) << "Unknown MPSolver basis status";
1272 return XPRS_FREE_SUPER;
1273 }
1274}
1275
1276// Returns the basis status of a row.
1278 if (mMip) {
1279 LOG(FATAL) << "Basis status only available for continuous problems";
1280 return MPSolver::FREE;
1281 }
1282
1284 if (mRstat.empty()) {
1285 int const rows = getnumrows(mLp);
1286 mRstat.resize(rows);
1287 CHECK_STATUS(XPRSgetbasis(mLp, mRstat.data(), 0));
1288 }
1289 } else {
1290 mRstat.clear();
1291 }
1292
1293 if (!mRstat.empty()) {
1294 return XpressToMPSolverBasisStatus(mRstat[constraint_index]);
1295 } else {
1296 LOG(FATAL) << "Row basis status not available";
1297 return MPSolver::FREE;
1298 }
1299}
1300
1301// Returns the basis status of a column.
1303 if (mMip) {
1304 LOG(FATAL) << "Basis status only available for continuous problems";
1305 return MPSolver::FREE;
1306 }
1307
1309 if (mCstat.empty()) {
1310 int const cols = getnumcols(mLp);
1311 mCstat.resize(cols);
1312 CHECK_STATUS(XPRSgetbasis(mLp, 0, mCstat.data()));
1313 }
1314 } else {
1315 mCstat.clear();
1316 }
1317
1318 if (!mCstat.empty()) {
1319 return XpressToMPSolverBasisStatus(mCstat[variable_index]);
1320 } else {
1321 LOG(FATAL) << "Column basis status not available";
1322 return MPSolver::FREE;
1323 }
1324}
1325
1326// Extract all variables that have not yet been extracted.
1328 // NOTE: The code assumes that a linear expression can never contain
1329 // non-zero duplicates.
1330
1332
1333 if (!supportIncrementalExtraction) {
1334 // Without incremental extraction ExtractModel() is always called
1335 // to extract the full model.
1336 CHECK(last_variable_index_ == 0 ||
1337 last_variable_index_ == solver_->variables_.size());
1338 CHECK(last_constraint_index_ == 0 ||
1339 last_constraint_index_ == solver_->constraints_.size());
1340 }
1341
1342 int const last_extracted = last_variable_index_;
1343 int const var_count = solver_->variables_.size();
1344 int new_col_count = var_count - last_extracted;
1345 if (new_col_count > 0) {
1346 // There are non-extracted variables. Extract them now.
1347
1348 unique_ptr<double[]> obj(new double[new_col_count]);
1349 unique_ptr<double[]> lb(new double[new_col_count]);
1350 unique_ptr<double[]> ub(new double[new_col_count]);
1351 unique_ptr<char[]> ctype(new char[new_col_count]);
1352
1353 for (int j = 0, var_idx = last_extracted; j < new_col_count;
1354 ++j, ++var_idx) {
1355 MPVariable const* const var = solver_->variables_[var_idx];
1356 lb[j] = var->lb();
1357 ub[j] = var->ub();
1358 ctype[j] = var->integer() ? XPRS_INTEGER : XPRS_CONTINUOUS;
1359 obj[j] = solver_->objective_->GetCoefficient(var);
1360 }
1361
1362 // Arrays for modifying the problem are setup. Update the index
1363 // of variables that will get extracted now. Updating indices
1364 // _before_ the actual extraction makes things much simpler in
1365 // case we support incremental extraction.
1366 // In case of error we just reset the indices.
1367 std::vector<MPVariable*> const& variables = solver_->variables();
1368 for (int j = last_extracted; j < var_count; ++j) {
1369 CHECK(!variable_is_extracted(variables[j]->index()));
1370 set_variable_as_extracted(variables[j]->index(), true);
1371 }
1372
1373 try {
1374 bool use_new_cols = true;
1375
1376 if (supportIncrementalExtraction) {
1377 // If we support incremental extraction then we must
1378 // update existing constraints with the new variables.
1379 // To do that we use XPRSaddcols() to actually create the
1380 // variables. This is supposed to be faster than combining
1381 // XPRSnewcols() and XPRSchgcoeflist().
1382
1383 // For each column count the size of the intersection with
1384 // existing constraints.
1385 unique_ptr<int[]> collen(new int[new_col_count]);
1386 for (int j = 0; j < new_col_count; ++j) collen[j] = 0;
1387 int nonzeros = 0;
1388 // TODO: Use a bitarray to flag the constraints that actually
1389 // intersect new variables?
1390 for (int i = 0; i < last_constraint_index_; ++i) {
1391 MPConstraint const* const ct = solver_->constraints_[i];
1392 CHECK(constraint_is_extracted(ct->index()));
1393 const auto& coeffs = ct->coefficients_;
1394 for (auto coeff : coeffs) {
1395 int const idx = coeff.first->index();
1396 if (variable_is_extracted(idx) && idx > last_variable_index_) {
1397 collen[idx - last_variable_index_]++;
1398 ++nonzeros;
1399 }
1400 }
1401 }
1402
1403 if (nonzeros > 0) {
1404 // At least one of the new variables did intersect with an
1405 // old constraint. We have to create the new columns via
1406 // XPRSaddcols().
1407 use_new_cols = false;
1408 unique_ptr<int[]> begin(new int[new_col_count + 2]);
1409 unique_ptr<int[]> cmatind(new int[nonzeros]);
1410 unique_ptr<double[]> cmatval(new double[nonzeros]);
1411
1412 // Here is how cmatbeg[] is setup:
1413 // - it is initialized as
1414 // [ 0, 0, collen[0], collen[0]+collen[1], ... ]
1415 // so that cmatbeg[j+1] tells us where in cmatind[] and
1416 // cmatval[] we need to put the next nonzero for column
1417 // j
1418 // - after nonzeros have been set up, the array looks like
1419 // [ 0, collen[0], collen[0]+collen[1], ... ]
1420 // so that it is the correct input argument for XPRSaddcols
1421 int* cmatbeg = begin.get();
1422 cmatbeg[0] = 0;
1423 cmatbeg[1] = 0;
1424 ++cmatbeg;
1425 for (int j = 0; j < new_col_count; ++j)
1426 cmatbeg[j + 1] = cmatbeg[j] + collen[j];
1427
1428 for (int i = 0; i < last_constraint_index_; ++i) {
1429 MPConstraint const* const ct = solver_->constraints_[i];
1430 int const row = ct->index();
1431 const auto& coeffs = ct->coefficients_;
1432 for (auto coeff : coeffs) {
1433 int const idx = coeff.first->index();
1434 if (variable_is_extracted(idx) && idx > last_variable_index_) {
1435 int const nz = cmatbeg[idx]++;
1436 cmatind[nz] = row;
1437 cmatval[nz] = coeff.second;
1438 }
1439 }
1440 }
1441 --cmatbeg;
1442 CHECK_STATUS(XPRSaddcols(mLp, new_col_count, nonzeros, obj.get(),
1443 cmatbeg, cmatind.get(), cmatval.get(),
1444 lb.get(), ub.get()));
1445 }
1446 }
1447
1448 if (use_new_cols) {
1449 // Either incremental extraction is not supported or none of
1450 // the new variables did intersect an existing constraint.
1451 // We can just use XPRSnewcols() to create the new variables.
1452 std::vector<int> collen(new_col_count, 0);
1453 std::vector<int> cmatbeg(new_col_count, 0);
1454 unique_ptr<int[]> cmatind(new int[1]);
1455 unique_ptr<double[]> cmatval(new double[1]);
1456 cmatind[0] = 0;
1457 cmatval[0] = 1.0;
1458
1459 CHECK_STATUS(XPRSaddcols(mLp, new_col_count, 0, obj.get(),
1460 cmatbeg.data(), cmatind.get(), cmatval.get(),
1461 lb.get(), ub.get()));
1462
1463 int const cols = getnumcols(mLp);
1464 unique_ptr<int[]> ind(new int[new_col_count]);
1465 for (int j = 0; j < cols; ++j) ind[j] = j;
1467 XPRSchgcoltype(mLp, cols - last_extracted, ind.get(), ctype.get()));
1468
1469 } else {
1470 // Incremental extraction: we must update the ctype of the
1471 // newly created variables (XPRSaddcols() does not allow
1472 // specifying the ctype)
1473 if (mMip && getnumcols(mLp) > 0) {
1474 // Query the actual number of columns in case we did not
1475 // manage to extract all columns.
1476 int const cols = getnumcols(mLp);
1477 unique_ptr<int[]> ind(new int[new_col_count]);
1478 for (int j = last_extracted; j < cols; ++j)
1479 ind[j - last_extracted] = j;
1480 CHECK_STATUS(XPRSchgcoltype(mLp, cols - last_extracted, ind.get(),
1481 ctype.get()));
1482 }
1483 }
1484 } catch (...) {
1485 // Undo all changes in case of error.
1486 int const cols = getnumcols(mLp);
1487 if (cols > last_extracted) {
1488 std::vector<int> cols_to_delete;
1489 for (int i = last_extracted; i < cols; ++i) cols_to_delete.push_back(i);
1490 (void)XPRSdelcols(mLp, cols_to_delete.size(), cols_to_delete.data());
1491 }
1492 std::vector<MPVariable*> const& variables = solver_->variables();
1493 int const size = variables.size();
1494 for (int j = last_extracted; j < size; ++j)
1495 set_variable_as_extracted(j, false);
1496 throw;
1497 }
1498 }
1499}
1500
1501// Extract constraints that have not yet been extracted.
1503 // NOTE: The code assumes that a linear expression can never contain
1504 // non-zero duplicates.
1505 if (!supportIncrementalExtraction) {
1506 // Without incremental extraction ExtractModel() is always called
1507 // to extract the full model.
1508 CHECK(last_variable_index_ == 0 ||
1509 last_variable_index_ == solver_->variables_.size());
1510 CHECK(last_constraint_index_ == 0 ||
1511 last_constraint_index_ == solver_->constraints_.size());
1512 }
1513
1514 int const offset = last_constraint_index_;
1515 int const total = solver_->constraints_.size();
1516
1517 if (total > offset) {
1518 // There are constraints that are not yet extracted.
1519
1521
1522 int newCons = total - offset;
1523 int const cols = getnumcols(mLp);
1524 int const chunk = newCons; // 10; // max number of rows to add in one shot
1525
1526 // Update indices of new constraints _before_ actually extracting
1527 // them. In case of error we will just reset the indices.
1528 for (int c = offset; c < total; ++c) set_constraint_as_extracted(c, true);
1529
1530 try {
1531 unique_ptr<int[]> rmatind(new int[cols]);
1532 unique_ptr<double[]> rmatval(new double[cols]);
1533 unique_ptr<int[]> rmatbeg(new int[chunk]);
1534 unique_ptr<char[]> sense(new char[chunk]);
1535 unique_ptr<double[]> rhs(new double[chunk]);
1536 unique_ptr<double[]> rngval(new double[chunk]);
1537
1538 // Loop over the new constraints, collecting rows for up to
1539 // CHUNK constraints into the arrays so that adding constraints
1540 // is faster.
1541 for (int c = 0; c < newCons; /* nothing */) {
1542 // Collect up to CHUNK constraints into the arrays.
1543 int nextRow = 0;
1544 int nextNz = 0;
1545 for (/* nothing */; c < newCons && nextRow < chunk; ++c, ++nextRow) {
1546 MPConstraint const* const ct = solver_->constraints_[offset + c];
1547
1548 // Stop if there is not enough room in the arrays
1549 // to add the current constraint.
1550 if (nextNz + ct->coefficients_.size() > cols) {
1551 DCHECK_GT(nextRow, 0);
1552 break;
1553 }
1554
1555 // Setup right-hand side of constraint.
1556 MakeRhs(ct->lb(), ct->ub(), rhs[nextRow], sense[nextRow],
1557 rngval[nextRow]);
1558
1559 // Setup left-hand side of constraint.
1560 rmatbeg[nextRow] = nextNz;
1561 const auto& coeffs = ct->coefficients_;
1562 for (auto coeff : coeffs) {
1563 int const idx = coeff.first->index();
1564 if (variable_is_extracted(idx)) {
1565 DCHECK_LT(nextNz, cols);
1566 DCHECK_LT(idx, cols);
1567 rmatind[nextNz] = idx;
1568 rmatval[nextNz] = coeff.second;
1569 ++nextNz;
1570 }
1571 }
1572 }
1573 if (nextRow > 0) {
1574 CHECK_STATUS(XPRSaddrows(mLp, nextRow, nextNz, sense.get(), rhs.get(),
1575 rngval.get(), rmatbeg.get(), rmatind.get(),
1576 rmatval.get()));
1577 }
1578 }
1579 } catch (...) {
1580 // Undo all changes in case of error.
1581 int const rows = getnumrows(mLp);
1582 std::vector<int> rows_to_delete;
1583 for (int i = offset; i < rows; ++i) rows_to_delete.push_back(i);
1584 if (rows > offset)
1585 (void)XPRSdelrows(mLp, rows_to_delete.size(), rows_to_delete.data());
1586 std::vector<MPConstraint*> const& constraints = solver_->constraints();
1587 int const size = constraints.size();
1588 for (int i = offset; i < size; ++i) set_constraint_as_extracted(i, false);
1589 throw;
1590 }
1591 }
1592}
1593
1594// Extract the objective function.
1596 // NOTE: The code assumes that the objective expression does not contain
1597 // any non-zero duplicates.
1598
1599 int const cols = getnumcols(mLp);
1600 // DCHECK_EQ(last_variable_index_, cols);
1601
1602 unique_ptr<int[]> ind(new int[cols]);
1603 unique_ptr<double[]> val(new double[cols]);
1604 for (int j = 0; j < cols; ++j) {
1605 ind[j] = j;
1606 val[j] = 0.0;
1607 }
1608
1609 const auto& coeffs = solver_->objective_->coefficients_;
1610 for (auto coeff : coeffs) {
1611 int const idx = coeff.first->index();
1612 if (variable_is_extracted(idx)) {
1613 DCHECK_LT(idx, cols);
1614 val[idx] = coeff.second;
1615 }
1616 }
1617
1618 CHECK_STATUS(XPRSchgobj(mLp, cols, ind.get(), val.get()));
1619 CHECK_STATUS(setobjoffset(mLp, solver_->Objective().offset()));
1620}
1621
1622// ------ Parameters -----
1623
1629
1631 if (mMip) {
1633 } else {
1634 LOG(WARNING) << "The relative MIP gap is only available "
1635 << "for discrete problems.";
1636 }
1637}
1638
1642
1646
1648 auto const presolve = static_cast<MPSolverParameters::PresolveValues>(value);
1649
1650 switch (presolve) {
1653 return;
1656 return;
1657 }
1659}
1660
1661// Sets the scaling mode.
1663 auto const scaling = static_cast<MPSolverParameters::ScalingValues>(value);
1664
1665 switch (scaling) {
1668 break;
1671 // In Xpress, scaling is not a binary on/off control, but a bit vector
1672 // control setting it to 1 would only enable bit 1. Instead, we reset it
1673 // to its default (163 for the current version 8.6) Alternatively, we
1674 // could call CHECK_STATUS(XPRSsetintcontrol(mLp, XPRS_SCALING, 163));
1675 break;
1676 }
1677}
1678
1679// Sets the LP algorithm : primal, dual or barrier. Note that XPRESS offers
1680// other LP algorithm (e.g. network) and automatic selection
1682 auto const algorithm =
1683 static_cast<MPSolverParameters::LpAlgorithmValues>(value);
1684
1685 int alg = 1;
1686
1687 switch (algorithm) {
1689 alg = 2;
1690 break;
1692 alg = 3;
1693 break;
1695 alg = 4;
1696 break;
1697 }
1698
1699 if (alg == XPRS_DEFAULTALG) {
1701 } else {
1703 }
1704}
1706 const std::vector<MPSolver::BasisStatus>& statuses) {
1707 std::vector<int> result;
1708 result.resize(statuses.size());
1709 std::transform(statuses.cbegin(), statuses.cend(), result.begin(),
1711 return result;
1712}
1713
1715 const std::vector<MPSolver::BasisStatus>& variable_statuses,
1716 const std::vector<MPSolver::BasisStatus>& constraint_statuses) {
1717 if (mMip) {
1718 LOG(DFATAL) << __FUNCTION__ << " is only available for LP problems";
1719 return;
1720 }
1721 initial_variables_basis_status_ = XpressBasisStatusesFrom(variable_statuses);
1722 initial_constraint_basis_status_ =
1723 XpressBasisStatusesFrom(constraint_statuses);
1724}
1725
1726bool XpressInterface::readParameters(std::istream& is, char sep) {
1727 // - parameters must be specified as NAME=VALUE
1728 // - settings must be separated by sep
1729 // - any whitespace is ignored
1730 // - string parameters are not supported
1731
1732 std::string name(""), value("");
1733 bool inValue = false;
1734
1735 while (is) {
1736 int c = is.get();
1737 if (is.eof()) break;
1738 if (c == '=') {
1739 if (inValue) {
1740 LOG(DFATAL) << "Failed to parse parameters in " << SolverVersion();
1741 return false;
1742 }
1743 inValue = true;
1744 } else if (c == sep) {
1745 // End of parameter setting
1746 if (name.size() == 0 && value.size() == 0) {
1747 // Ok to have empty "lines".
1748 continue;
1749 } else if (name.size() == 0) {
1750 LOG(DFATAL) << "Parameter setting without name in " << SolverVersion();
1751 } else if (!readParameter(mLp, name, value))
1752 return false;
1753
1754 // Reset for parsing the next parameter setting.
1755 name = "";
1756 value = "";
1757 inValue = false;
1758 } else if (std::isspace(c)) {
1759 continue;
1760 } else if (inValue) {
1761 value += (char)c;
1762 } else {
1763 name += (char)c;
1764 }
1765 }
1766 if (inValue) return readParameter(mLp, name, value);
1767
1768 return true;
1769}
1770
1771bool XpressInterface::ReadParameterFile(std::string const& filename) {
1772 // Return true on success and false on error.
1773 std::ifstream s(filename);
1774 if (!s) return false;
1775 return readParameters(s, '\n');
1776}
1777
1779 return ".prm";
1780}
1781
1783 int status;
1784
1785 // Delete cached information
1786 mCstat.clear();
1787 mRstat.clear();
1788
1789 WallTimer timer;
1790 timer.Start();
1791
1792 // Set incrementality
1793 auto const inc = static_cast<MPSolverParameters::IncrementalityValues>(
1795 switch (inc) {
1797 Reset(); // This should not be required but re-extracting everything
1798 // may be faster, so we do it.
1799 break;
1800 }
1803 break;
1804 }
1805 }
1806
1807 // Extract the model to be solved.
1808 // If we don't support incremental extraction and the low-level modeling
1809 // is out of sync then we have to re-extract everything. Note that this
1810 // will lose MIP starts or advanced basis information from a previous
1811 // solve.
1812 if (!supportIncrementalExtraction && sync_status_ == MUST_RELOAD) Reset();
1813 ExtractModel();
1814 VLOG(1) << absl::StrFormat("Model build in %.3f seconds.", timer.Get());
1815
1816 // Set log level.
1817 XPRSsetintcontrol(mLp, XPRS_OUTPUTLOG, quiet() ? 0 : 1);
1818 // Set parameters.
1819 // We first set our internal MPSolverParameters from 'param' and then set
1820 // any user-specified internal solver parameters via
1821 // solver_specific_parameter_string_.
1822 // Default MPSolverParameters can override custom parameters while specific
1823 // parameters allow a higher level of customization (for example for
1824 // presolving) and therefore we apply MPSolverParameters first.
1825 SetParameters(param);
1826 solver_->SetSolverSpecificParametersAsString(
1827 solver_->solver_specific_parameter_string_);
1828 if (solver_->time_limit()) {
1829 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
1830 // In Xpress, a time limit should usually have a negative sign. With a
1831 // positive sign, the solver will only stop when a solution has been found.
1833 -1 * solver_->time_limit_in_secs()));
1834 }
1835
1836 // Load basis if present
1837 // TODO : check number of variables / constraints
1838 if (!mMip && !initial_variables_basis_status_.empty() &&
1839 !initial_constraint_basis_status_.empty()) {
1840 CHECK_STATUS(XPRSloadbasis(mLp, initial_constraint_basis_status_.data(),
1841 initial_variables_basis_status_.data()));
1842 }
1843
1844 // Set the hint (if any)
1845 this->AddSolutionHintToOptimizer();
1846
1847 // Add opt node callback to optimizer. We have to do this here (just before
1848 // solve) to make sure the variables are fully initialized
1849 MPCallbackWrapper* mp_callback_wrapper = nullptr;
1850 if (callback_ != nullptr) {
1851 mp_callback_wrapper = new MPCallbackWrapper(callback_);
1853 static_cast<void*>(mp_callback_wrapper), 0));
1854 }
1855
1856 // Solve.
1857 // Do not CHECK_STATUS here since some errors (for example CPXERR_NO_MEMORY)
1858 // still allow us to query useful information.
1859 timer.Restart();
1860
1861 int xpress_stat = 0;
1862 if (mMip) {
1863 status = XPRSmipoptimize(mLp, "");
1864 XPRSgetintattrib(mLp, XPRS_MIPSTATUS, &xpress_stat);
1865 } else {
1866 status = XPRSlpoptimize(mLp, "");
1867 XPRSgetintattrib(mLp, XPRS_LPSTATUS, &xpress_stat);
1868 }
1869
1870 if (mp_callback_wrapper != nullptr) {
1871 mp_callback_wrapper->LogCaughtExceptions();
1872 delete mp_callback_wrapper;
1873 }
1874
1875 if (!(mMip ? (xpress_stat == XPRS_MIP_OPTIMAL)
1876 : (xpress_stat == XPRS_LP_OPTIMAL))) {
1877 XPRSpostsolve(mLp);
1878 }
1879
1880 // Disable screen output right after solve
1882
1883 if (status) {
1884 VLOG(1) << absl::StrFormat("Failed to optimize MIP. Error %d", status);
1885 // NOTE: We do not return immediately since there may be information
1886 // to grab (for example an incumbent)
1887 } else {
1888 VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get());
1889 }
1890
1891 VLOG(1) << absl::StrFormat("XPRESS solution status %d.", xpress_stat);
1892
1893 // Figure out what solution we have.
1894 bool const feasible = (mMip ? (xpress_stat == XPRS_MIP_OPTIMAL ||
1895 xpress_stat == XPRS_MIP_SOLUTION)
1896 : (!mMip && xpress_stat == XPRS_LP_OPTIMAL));
1897
1898 // Get problem dimensions for solution queries below.
1899 int const rows = getnumrows(mLp);
1900 int const cols = getnumcols(mLp);
1901 DCHECK_EQ(rows, solver_->constraints_.size());
1902 DCHECK_EQ(cols, solver_->variables_.size());
1903
1904 // Capture objective function value.
1907 if (feasible) {
1908 if (mMip) {
1912 } else {
1914 }
1915 }
1916 VLOG(1) << "objective=" << objective_value_
1917 << ", bound=" << best_objective_bound_;
1918
1919 // Capture primal and dual solutions
1920 if (mMip) {
1921 // If there is a primal feasible solution then capture it.
1922 if (feasible) {
1923 if (cols > 0) {
1924 unique_ptr<double[]> x(new double[cols]);
1925 CHECK_STATUS(XPRSgetmipsol(mLp, x.get(), 0));
1926 for (int i = 0; i < solver_->variables_.size(); ++i) {
1927 MPVariable* const var = solver_->variables_[i];
1928 var->set_solution_value(x[i]);
1929 VLOG(3) << var->name() << ": value =" << x[i];
1930 }
1931 }
1932 } else {
1933 for (auto& variable : solver_->variables_)
1934 variable->set_solution_value(XPRS_NAN);
1935 }
1936
1937 // MIP does not have duals
1938 for (auto& variable : solver_->variables_)
1939 variable->set_reduced_cost(XPRS_NAN);
1940 for (auto& constraint : solver_->constraints_)
1941 constraint->set_dual_value(XPRS_NAN);
1942 } else {
1943 // Continuous problem.
1944 if (cols > 0) {
1945 unique_ptr<double[]> x(new double[cols]);
1946 unique_ptr<double[]> dj(new double[cols]);
1947 if (feasible) CHECK_STATUS(XPRSgetlpsol(mLp, x.get(), 0, 0, dj.get()));
1948 for (int i = 0; i < solver_->variables_.size(); ++i) {
1949 MPVariable* const var = solver_->variables_[i];
1950 var->set_solution_value(x[i]);
1951 bool value = false, dual = false;
1952
1953 if (feasible) {
1954 var->set_solution_value(x[i]);
1955 value = true;
1956 } else {
1958 }
1959 if (feasible) {
1960 var->set_reduced_cost(dj[i]);
1961 dual = true;
1962 } else {
1964 }
1965 VLOG(3) << var->name() << ":"
1966 << (value ? absl::StrFormat(" value = %f", x[i]) : "")
1967 << (dual ? absl::StrFormat(" reduced cost = %f", dj[i]) : "");
1968 }
1969 }
1970
1971 if (rows > 0) {
1972 unique_ptr<double[]> pi(new double[rows]);
1973 if (feasible) {
1974 CHECK_STATUS(XPRSgetlpsol(mLp, 0, 0, pi.get(), 0));
1975 }
1976 for (int i = 0; i < solver_->constraints_.size(); ++i) {
1977 MPConstraint* const ct = solver_->constraints_[i];
1978 bool dual = false;
1979 if (feasible) {
1980 ct->set_dual_value(pi[i]);
1981 dual = true;
1982 } else {
1984 }
1985 VLOG(4) << "row " << ct->index() << ":"
1986 << (dual ? absl::StrFormat(" dual = %f", pi[i]) : "");
1987 }
1988 }
1989 }
1990
1991 // Map XPRESS status to more generic solution status in MPSolver
1992 if (mMip) {
1993 switch (xpress_stat) {
1994 case XPRS_MIP_OPTIMAL:
1996 break;
1997 case XPRS_MIP_INFEAS:
1999 break;
2000 case XPRS_MIP_UNBOUNDED:
2002 break;
2003 default:
2005 break;
2006 }
2007 } else {
2008 switch (xpress_stat) {
2009 case XPRS_LP_OPTIMAL:
2011 break;
2012 case XPRS_LP_INFEAS:
2014 break;
2015 case XPRS_LP_UNBOUNDED:
2017 break;
2018 default:
2020 break;
2021 }
2022 }
2023
2025 return result_status_;
2026}
2027
2028namespace {
2029template <class T>
2030struct getNameFlag;
2031
2032template <>
2033struct getNameFlag<MPVariable> {
2034 enum { value = XPRS_NAMES_COLUMN };
2035};
2036
2037template <>
2038struct getNameFlag<MPConstraint> {
2039 enum { value = XPRS_NAMES_ROW };
2040};
2041
2042template <class T>
2043// T = MPVariable | MPConstraint
2044// or any class that has a public method name() const
2045void ExtractNames(XPRSprob mLp, const std::vector<T*>& objects) {
2046 const bool have_names =
2047 std::any_of(objects.begin(), objects.end(),
2048 [](const T* x) { return !x->name().empty(); });
2049
2050 // FICO XPRESS requires a single large const char* such as
2051 // "name1\0name2\0name3"
2052 // See
2053 // https://www.fico.com/fico-xpress-optimization/docs/latest/solver/optimizer/HTML/XPRSaddnames.html
2054 if (have_names) {
2055 std::vector<char> all_names;
2056 for (const auto& x : objects) {
2057 const std::string& current_name = x->name();
2058 std::copy(current_name.begin(), current_name.end(),
2059 std::back_inserter(all_names));
2060 all_names.push_back('\0');
2061 }
2062
2063 // Remove trailing '\0', if any
2064 // Note : Calling pop_back on an empty container is undefined behavior.
2065 if (!all_names.empty() && all_names.back() == '\0') all_names.pop_back();
2066
2067 CHECK_STATUS(XPRSaddnames(mLp, getNameFlag<T>::value, all_names.data(), 0,
2068 objects.size() - 1));
2069 }
2070}
2071} // namespace
2072
2073void XpressInterface::Write(const std::string& filename) {
2074 if (sync_status_ == MUST_RELOAD) {
2075 Reset();
2076 }
2077 ExtractModel();
2078
2079 ExtractNames(mLp, solver_->variables_);
2080 ExtractNames(mLp, solver_->constraints_);
2081
2082 VLOG(1) << "Writing Xpress MPS \"" << filename << "\".";
2083 const int status = XPRSwriteprob(mLp, filename.c_str(), "");
2084 if (status) {
2085 LOG(ERROR) << "Xpress: Failed to write MPS!";
2086 }
2087}
2088
2090 return new XpressInterface(solver, mip);
2091}
2092// TODO useless ?
2093template <class Container>
2094void splitMyString(const std::string& str, Container& cont, char delim = ' ') {
2095 std::stringstream ss(str);
2096 std::string token;
2097 while (std::getline(ss, token, delim)) {
2098 cont.push_back(token);
2099 }
2100}
2101
2102const char* stringToCharPtr(std::string& var) { return var.c_str(); }
2103
2104// Save the existing locale, use the "C" locale to ensure that
2105// string -> double conversion is done ignoring the locale.
2108 oldLocale = std::setlocale(LC_NUMERIC, nullptr);
2109 auto newLocale = std::setlocale(LC_NUMERIC, "C");
2110 CHECK_EQ(std::string(newLocale), "C");
2111 }
2112 ~ScopedLocale() { std::setlocale(LC_NUMERIC, oldLocale); }
2113
2114 private:
2115 const char* oldLocale;
2116};
2117
2118#define setParamIfPossible_MACRO(target_map, setter, converter) \
2119 { \
2120 auto matchingParamIter = (target_map).find(paramAndValuePair.first); \
2121 if (matchingParamIter != (target_map).end()) { \
2122 const auto convertedValue = converter(paramAndValuePair.second); \
2123 VLOG(1) << "Setting parameter " << paramAndValuePair.first \
2124 << " to value " << convertedValue << std::endl; \
2125 setter(mLp, matchingParamIter->second, convertedValue); \
2126 continue; \
2127 } \
2128 }
2129
2130bool XpressInterface::SetSolverSpecificParametersAsString(
2131 const std::string& parameters) {
2132 if (parameters.empty()) return true;
2133
2134 std::vector<std::pair<std::string, std::string> > paramAndValuePairList;
2135
2136 std::stringstream ss(parameters);
2137 std::string paramName;
2138 while (std::getline(ss, paramName, ' ')) {
2139 std::string paramValue;
2140 if (std::getline(ss, paramValue, ' ')) {
2141 paramAndValuePairList.push_back(std::make_pair(paramName, paramValue));
2142 } else {
2143 LOG(ERROR) << "No value for parameter " << paramName << " : function "
2144 << __FUNCTION__ << std::endl;
2145 return false;
2146 }
2147 }
2148
2149 ScopedLocale locale;
2150 for (auto& paramAndValuePair : paramAndValuePairList) {
2151 setParamIfPossible_MACRO(mapIntegerControls_, XPRSsetintcontrol, std::stoi);
2152 setParamIfPossible_MACRO(mapDoubleControls_, XPRSsetdblcontrol, std::stod);
2153 setParamIfPossible_MACRO(mapStringControls_, XPRSsetstrcontrol,
2155 setParamIfPossible_MACRO(mapInteger64Controls_, XPRSsetintcontrol64,
2156 std::stoll);
2157 LOG(ERROR) << "Unknown parameter " << paramName << " : function "
2158 << __FUNCTION__ << std::endl;
2159 return false;
2160 }
2161 return true;
2162}
2163
2164/*****************************************************************************\
2165* Name: optimizermsg
2166* Purpose: Display Optimizer error messages and warnings.
2167* Arguments: const char *sMsg Message string
2168* int nLen Message length
2169* int nMsgLvl Message type
2170* Return Value: None
2171\*****************************************************************************/
2172void XPRS_CC optimizermsg(XPRSprob prob, void* data, const char* sMsg, int nLen,
2173 int nMsgLvl) {
2174 auto* xprs = reinterpret_cast<operations_research::XpressInterface*>(data);
2175 if (!xprs->quiet()) {
2176 switch (nMsgLvl) {
2177 /* Print Optimizer error messages and warnings */
2178 case 4: /* error */
2179 case 3: /* warning */
2180 /* Ignore other messages */
2181 case 2: /* dialogue */
2182 case 1: /* information */
2183 printf("%*s\n", nLen, sMsg);
2184 break;
2185 /* Exit and flush buffers */
2186 default:
2187 fflush(nullptr);
2188 break;
2189 }
2190 }
2191}
2192
2193void XpressInterface::AddSolutionHintToOptimizer() {
2194 // Currently the XPRESS API does not handle clearing out previous hints
2195 const std::size_t len = solver_->solution_hint_.size();
2196 if (len == 0) {
2197 // hint is empty, nothing to do
2198 return;
2199 }
2200 unique_ptr<int[]> col_ind(new int[len]);
2201 unique_ptr<double[]> val(new double[len]);
2202
2203 for (std::size_t i = 0; i < len; ++i) {
2204 col_ind[i] = solver_->solution_hint_[i].first->index();
2205 val[i] = solver_->solution_hint_[i].second;
2206 }
2207 addhint(mLp, len, val.get(), col_ind.get());
2208}
2209
2211 if (callback_ != nullptr) {
2212 // replace existing callback by removing it first
2214 }
2215 callback_ = mp_callback;
2216}
2217
2218// This is the call-back called by XPRESS when it finds a new MIP solution
2219// NOTE(user): This function must have this exact API, because we are passing
2220// it to XPRESS as a callback.
2221void XPRS_CC XpressIntSolCallbackImpl(XPRSprob cbprob, void* cbdata) {
2222 auto callback_with_context = static_cast<MPCallbackWrapper*>(cbdata);
2223 if (callback_with_context == nullptr ||
2224 callback_with_context->GetCallback() == nullptr) {
2225 // nothing to do
2226 return;
2227 }
2228 try {
2229 std::unique_ptr<XpressMPCallbackContext> cb_context =
2230 std::make_unique<XpressMPCallbackContext>(
2231 &cbprob, MPCallbackEvent::kMipSolution, getnodecnt(cbprob));
2232 callback_with_context->GetCallback()->RunCallback(cb_context.get());
2233 } catch (std::exception&) {
2234 callback_with_context->CatchException(cbprob);
2235 }
2236}
2237
2241
2243 if (variable_values_.empty()) {
2244 int num_vars = getnumcols(*xprsprob_);
2245 variable_values_.resize(num_vars);
2246 CHECK_STATUS(XPRSgetmipsol(*xprsprob_, variable_values_.data(), 0));
2247 }
2248 return variable_values_[variable->index()];
2249}
2250
2252 const absl::flat_hash_map<const MPVariable*, double>& solution) {
2253 // Currently the XPRESS API does not handle clearing out previous hints
2254 const std::size_t len = solution.size();
2255 if (len == 0) {
2256 // hint is empty, do nothing
2257 return NAN;
2258 }
2260 // Currently, XPRESS does not handle adding a new MIP solution inside the
2261 // "cbintsol" callback (cb for new MIP solutions that is used here)
2262 // So we have to prevent the user from adding a solution
2263 // TODO: remove this workaround when it is handled in XPRESS
2264 LOG(WARNING)
2265 << "XPRESS does not currently allow suggesting MIP solutions after "
2266 "a kMipSolution event. Try another call-back.";
2267 return NAN;
2268 }
2269 unique_ptr<int[]> colind(new int[len]);
2270 unique_ptr<double[]> val(new double[len]);
2271 int i = 0;
2272 for (const auto& [var, value] : solution) {
2273 colind[i] = var->index();
2274 val[i] = value;
2275 ++i;
2276 }
2277 addhint(*xprsprob_, len, val.get(), colind.get());
2278
2279 // XPRESS doesn't guarantee if nor when it will test the suggested solution.
2280 // So we return NaN because we can't know the actual objective value.
2281 return NAN;
2282}
2283
2284} // namespace operations_research
double Get() const
Definition timer.h:46
void Restart()
Definition timer.h:36
void Start()
When Start() is called multiple times, only the most recent is used.
Definition timer.h:32
Wraps the MPCallback in order to catch and store exceptions.
void set_dual_value(double dual_value)
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
void set_variable_as_extracted(int var_index, bool extracted)
static constexpr int64_t kUnknownNumberOfIterations
friend class MPConstraint
To access the maximize_ bool and the MPSolver.
void set_constraint_as_extracted(int ct_index, bool extracted)
void ResetExtractionInformation()
Resets the extraction information.
int last_variable_index_
Index in MPSolver::constraints_ of last variable extracted.
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
Sets a supported integer parameter to an unsupported value.
int last_constraint_index_
Index in MPSolver::variables_ of last constraint extracted.
bool variable_is_extracted(int var_index) const
bool quiet() const
Returns the boolean indicating the verbosity of the solver output.
bool constraint_is_extracted(int ct_index) const
static constexpr int64_t kUnknownNumberOfNodes
void ExtractModel()
Extracts model stored in MPSolver.
double objective_value_
The value of the objective function.
double best_objective_bound_
The value of the best objective bound. Used only for MIP solvers.
bool maximize_
Optimization direction.
void SetMIPParameters(const MPSolverParameters &param)
Sets MIP specific parameters in the underlying solver.
void SetCommonParameters(const MPSolverParameters &param)
Sets parameters common to LP and MIP in the underlying solver.
SynchronizationStatus sync_status_
Indicates whether the model and the solution are synchronized.
PresolveValues
For each categorical parameter, enumeration of possible values.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
@ PRESOLVE
Advanced usage: presolve mode.
@ LP_ALGORITHM
Algorithm to solve linear programs.
@ SCALING
Advanced usage: enable or disable matrix scaling.
IncrementalityValues
Advanced usage: Incrementality options.
@ INCREMENTALITY_OFF
Start solve from scratch.
ScalingValues
Advanced usage: Scaling options.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
@ FEASIBLE
feasible, or stopped by limit.
@ INFEASIBLE
proven infeasible.
@ ABNORMAL
abnormal, i.e., error of some kind.
The class for variables of a Mathematical Programming (MP) model.
bool integer() const
Returns the integrality requirement of the variable.
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
const std::string & name() const
Returns the name of the variable.
void set_reduced_cost(double reduced_cost)
void set_solution_value(double value)
int index() const
Returns the index of the variable in the MPSolver::variables_.
MPSolver::BasisStatus row_status(int constraint_index) const override
Returns the basis status of a row.
virtual int64_t nodes() const
Number of branch-and-bound nodes. Only available for discrete problems.
MPSolver::BasisStatus column_status(int variable_index) const override
Returns the basis status of a column.
void ClearObjective() override
Clear the objective from all its terms.
void SetOptimizationDirection(bool maximize) override
Sets the optimization direction (min/max).
void AddRowConstraint(MPConstraint *ct) override
Adds a linear constraint.
void SetObjectiveOffset(double value) override
Change the constant term in the linear objective.
double ComputeExactConditionNumber() const override
bool IsLP() const override
Returns true if the problem is continuous and linear.
virtual bool ReadParameterFile(std::string const &filename)
void Reset() override
---— Model modifications and extraction --—
void SetCoefficient(MPConstraint *constraint, MPVariable const *variable, double new_value, double old_value) override
Changes a coefficient in a constraint.
void SetVariableBounds(int var_index, double lb, double ub) override
Modifies bounds of an extracted variable.
XpressInterface(MPSolver *solver, bool mip)
Creates an LP/MIP instance.
void Write(const std::string &filename) override
Writes the model.
void * underlying_solver() override
Returns the underlying solver.
MPSolver::ResultStatus Solve(MPSolverParameters const &param) override
void ExtractObjective() override
Extract the objective function.
void ClearConstraint(MPConstraint *constraint) override
Clear a constraint from all its terms.
void SetConstraintBounds(int row_index, double lb, double ub) override
Modify bounds of an extracted variable.
void SetRelativeMipGap(double value) override
Set each parameter in the underlying solver.
void SetDualTolerance(double value) override
void ExtractNewVariables() override
Extract all variables that have not yet been extracted.
void SetScalingMode(int value) override
Sets the scaling mode.
void AddVariable(MPVariable *var) override
Add a variable.
void SetObjectiveCoefficient(MPVariable const *variable, double coefficient) override
Change a coefficient in the linear objective.
void SetStartingLpBasis(const std::vector< MPSolver::BasisStatus > &variable_statuses, const std::vector< MPSolver::BasisStatus > &constraint_statuses) override
See MPSolver::SetStartingLpBasis().
void SetCallback(MPCallback *mp_callback) override
See MPSolver::SetCallback() for details.
virtual int64_t iterations() const
---— Query statistics on the solution and the solve ---—
void ExtractNewConstraints() override
Extract constraints that have not yet been extracted.
virtual std::string ValidFileExtensionForParameterFile() const
void SetPrimalTolerance(double value) override
void SetParameters(MPSolverParameters const &param) override
Set all parameters in the underlying solver.
void SetVariableInteger(int var_index, bool integer) override
Modifies integrality of an extracted variable.
bool IsMIP() const override
Returns true if the problem is discrete and linear.
bool IsContinuous() const override
--— Misc --—
std::string SolverVersion() const override
Returns a string describing the underlying solver and its version.
void AddCut(const LinearRange &cutting_plane) override
MPCallbackEvent Event() override
Implementation of the interface.
XpressMPCallbackContext(XPRSprob *xprsprob, MPCallbackEvent event, int num_nodes)
double SuggestSolution(const absl::flat_hash_map< const MPVariable *, double > &solution) override
void AddLazyConstraint(const LinearRange &lazy_constraint) override
double VariableValue(const MPVariable *variable) override
#define CHECK_STATUS(s)
In SWIG mode, we don't want anything besides these top-level includes.
std::function< int(XPRSprob prob, void(XPRS_CC *f_intsol)(XPRSprob cbprob, void *cbdata), void *p, int priority)> XPRSaddcbintsol
std::function< int(XPRSprob prob, int control)> XPRSsetdefaultcontrol
std::function< int(XPRSprob prob, int nrows, const int rowind[], const char rowtype[])> XPRSchgrowtype
void addhint(const XPRSprob &mLp, int length, const double solval[], const int colind[])
std::function< int(XPRSprob prob, int attrib, int *p_value)> XPRSgetintattrib
static std::map< std::string, int > & getMapIntControls()
std::function< int(XPRSprob prob, int attrib, double *p_value)> XPRSgetdblattrib
std::function< int(XPRSprob prob, const char *filename, const char *flags)> XPRSwriteprob
std::function< int(XPRSprob prob, void(XPRS_CC *f_message)(XPRSprob cbprob, void *cbdata, const char *msg, int msglen, int msgtype), void *p, int priority)> XPRSaddcbmessage
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
static MPSolver::BasisStatus XpressToMPSolverBasisStatus(int xpress_basis_status)
Transform XPRESS basis status to MPSolver basis status.
bool initXpressEnv(bool verbose, int xpress_oem_license_key)
! init XPRESS environment.
std::function< int(XPRSprob prob, int length, const double solval[], const int colind[], const char *name)> XPRSaddmipsol
int getnumcols(const XPRSprob &mLp)
std::function< int(XPRSprob prob, double x[], double slack[])> XPRSgetmipsol
std::function< int(XPRSprob prob, int nbounds, const int colind[], const char bndtype[], const double bndval[])> XPRSchgbounds
int getnodecnt(const XPRSprob &mLp)
std::function< int(XPRSprob prob, const int rowstat[], const int colstat[])> XPRSloadbasis
std::function< int(XPRSprob prob, int row, int col, double coef)> XPRSchgcoef
std::function< int(XPRSprob prob, int nrows, const int rowind[])> XPRSdelrows
std::function< int(XPRSprob prob, int ncols, const int colind[])> XPRSdelcols
std::string getSolverVersion(XPRSprob const &prob)
void interruptXPRESS(XPRSprob &xprsProb, CUSTOM_INTERRUPT_REASON reason)
std::function< int(XPRSprob prob, int control, int *p_value)> XPRSgetintcontrol
std::function< int(XPRSprob prob, void(XPRS_CC *f_intsol)(XPRSprob cbprob, void *cbdata), void *p)> XPRSremovecbintsol
@ kMipSolution
Called every time a new MIP incumbent is found.
std::function< int(XPRSprob prob, int ncols, const int colind[], const char coltype[])> XPRSchgcoltype
std::function< int(XPRSprob prob, const char *name, int *p_id, int *p_type)> XPRSgetcontrolinfo
std::function< int(XPRSprob prob, double x[], double slack[], double duals[], double djs[])> XPRSgetlpsol
std::function< int(XPRSprob prob)> XPRSpostsolve
std::function< int(XPRSprob prob, int nrows, const int rowind[], const double rng[])> XPRSchgrhsrange
int setobjoffset(const XPRSprob &mLp, double value)
std::function< int(XPRSprob prob, int type, const char names[], int first, int last)> XPRSaddnames
static int MPSolverToXpressBasisStatus(MPSolver::BasisStatus mpsolver_basis_status)
Transform MPSolver basis status to XPRESS status.
std::function< int(XPRSprob prob, int ncoefs, const int rowind[], const int colind[], const double rowcoef[])> XPRSchgmcoef
std::function< int(XPRSprob prob, const char *probname, int ncols, int nrows, const char rowtype[], const double rhs[], const double rng[], const double objcoef[], const int start[], const int collen[], const int rowind[], const double rowcoef[], const double lb[], const double ub[])> XPRSloadlp
std::function< int(void)> XPRSfree
std::function< int(XPRSprob prob, int reason)> XPRSinterrupt
int getitcnt(const XPRSprob &mLp)
std::vector< int > XpressBasisStatusesFrom(const std::vector< MPSolver::BasisStatus > &statuses)
MPSolverInterface * BuildXpressInterface(bool mip, MPSolver *const solver)
void splitMyString(const std::string &str, Container &cont, char delim=' ')
std::function< int(XPRSprob prob, int control, int value)> XPRSsetintcontrol
std::function< int(XPRSprob prob, int nrows, int ncoefs, const char rowtype[], const double rhs[], const double rng[], const int start[], const int colind[], const double rowcoef[])> XPRSaddrows
static std::map< std::string, int > & getMapStringControls()
std::function< int(XPRSprob prob, char *errmsg)> XPRSgetlasterror
const char * stringToCharPtr(std::string &var)
void XPRS_CC XpressIntSolCallbackImpl(XPRSprob cbprob, void *cbdata)
std::function< int(XPRSprob prob)> XPRSdestroyprob
int getnumrows(const XPRSprob &mLp)
void printError(const XPRSprob &mLp, int line)
void XPRS_CC optimizermsg(XPRSprob prob, void *data, const char *sMsg, int nLen, int nMsgLvl)
std::function< int(XPRSprob prob, int control, double value)> XPRSsetdblcontrol
std::function< int(XPRSprob prob, int control, const char *value)> XPRSsetstrcontrol
std::function< int(XPRSprob prob, int ncols, int ncoefs, const double objcoef[], const int start[], const int rowind[], const double rowcoef[], const double lb[], const double ub[])> XPRSaddcols
static std::map< std::string, int > & getMapDoubleControls()
std::function< int(XPRSprob prob, const char *flags)> XPRSmipoptimize
std::function< int(XPRSprob prob, int nrows, const int rowind[], const double rhs[])> XPRSchgrhs
std::function< int(XPRSprob prob, int ncols, const int colind[], const double objcoef[])> XPRSchgobj
std::function< int(XPRSprob *p_prob)> XPRScreateprob
This is the 'define' section.
std::function< int(XPRSprob prob, const char *flags)> XPRSlpoptimize
static std::map< std::string, int > & getMapInt64Controls()
std::function< int(XPRSprob prob, int control, XPRSint64 value)> XPRSsetintcontrol64
std::function< int(XPRSprob prob, int objsense)> XPRSchgobjsense
std::function< int(XPRSprob prob, int rowstat[], int colstat[])> XPRSgetbasis
bool readParameter(XPRSprob const &prob, std::string const &name, std::string const &value)
Apply the specified name=value setting to prob.
#define XPRS_COMPUTEEXECSERVICE
#define XPRS_INPUTTOL
#define XPRS_BARORDERTHREADS
#define XPRS_EXTRAMIPENTS
#define XPRS_PREPERMUTE
#define XPRS_MPSOBJNAME
Definition environment.h:97
#define XPRS_MAXMEMORYHARD
#define XPRS_HEURSEARCHTARGETSIZE
#define XPRS_RELAXTREEMEMORYLIMIT
#define XPRS_PRESOLVEOPS
#define XPRS_CROSSOVERRELPIVOTTOL
#define XPRS_BARGAPTARGET
#define XPRS_TREECUTSELECT
#define XPRS_SIMPLEXITER
#define XPRS_SBEFFORT
#define XPRS_TUNERTHREADS
#define XPRS_BARDUALSTOP
#define XPRS_MAXPAGELINES
#define XPRS_HEURMAXSOL
#define XPRS_L1CACHE
#define XPRS_MAXCHECKSONMAXTIME
#define XPRS_HEURDIVEITERLIMIT
#define XPRS_GLOBALBOUNDINGBOX
#define XPRS_FEASTOLPERTURB
#define XPRS_TREEFILELOGINTERVAL
#define XPRS_BAROBJSCALE
#define XPRS_HEURSEARCHTREESELECT
#define XPRS_RESOURCESTRATEGY
#define XPRS_INVERTMIN
#define XPRS_TREEMEMORYSAVINGTARGET
#define XPRS_ROWS
#define XPRS_QUADRATICUNSHIFT
#define XPRS_MIPABSGAPNOTIFYOBJ
#define XPRS_LPSTATUS
#define XPRS_CROSSOVERITERLIMIT
#define XPRS_TYPE_NOTDEFINED
Definition environment.h:81
#define XPRS_CLAMPING
#define XPRS_TYPE_INT
Definition environment.h:82
#define XPRS_CPIALPHA
#define XPRS_MIPKAPPAFREQ
#define XPRS_CROSSOVERFEASWEIGHT
#define XPRS_REFINEOPS
#define XPRS_BRANCHCHOICE
#define XPRS_OBJ_MAXIMIZE
#define XPRS_FEASIBILITYJUMP
#define XPRS_OUTPUTCONTROLS
#define XPRS_LP_INFEAS
#define XPRS_PREBASISRED
#define XPRS_INVERTFREQ
#define XPRS_COMPUTEMATX
#define XPRS_MIP_SOLUTION
#define XPRS_HEURDEPTH
#define XPRS_BARPRESOLVEOPS
#define XPRS_MIPABSGAPNOTIFY
#define XPRS_HEURSEARCHFREQ
#define XPRS_BARINDEFLIMIT
#define XPRS_SIFTPRESOLVEOPS
#define XPRS_HEURNODES
#define XPRS_VARSELECTION
#define XPRS_NAMES_ROW
Definition environment.h:89
#define XPRS_CHECKINPUTDATA
#define XPRS_PLUSINFINITY
Definition environment.h:92
#define XPRS_MIP_OPTIMAL
#define XPRS_SCALING
#define XPRS_TRACE
#define XPRS_PRIMALOPS
#define XPRS_NETCUTS
#define XPRS_EIGENVALUETOL
#define XPRS_DUMMYCONTROL
#define XPRS_BARSOLUTION
#define XPRS_CPUPLATFORM
#define XPRS_MIPTOL
#define XPRS_COMPUTEJOBPRIORITY
#define XPRS_CRASH
#define XPRS_NUMERICALEMPHASIS
#define XPRS_QCROOTALG
#define XPRS_MAXLOCALBACKTRACK
#define XPRS_RANDOMSEED
#define XPRS_CROSSOVERRELPIVOTTOLSAFE
#define XPRS_BASIC
#define XPRS_IFCHECKCONVEXITY
#define XPRS_MIPRESTARTFACTOR
#define XPRS_MIPSTATUS
#define XPRS_MIPABSSTOP
#define XPRS_MIPADDCUTOFF
#define XPRS_SERIALIZEPREINTSOL
#define XPRS_BARGAPSTOP
#define XPRS_HEURFORCESPECIALOBJ
#define XPRS_ALGAFTERCROSSOVER
#define XPRS_HEURDIVESPEEDUP
#define XPRS_MAXMIPTASKS
#define XPRS_BACKTRACKTIE
#define XPRS_MIP_UNBOUNDED
#define XPRS_QSIMPLEXOPS
#define XPRS_COLS
#define XPRS_PRECONVERTSEPARABLE
#define XPRS_PRECOMPONENTSEFFORT
#define XPRS_COMPUTEMATX_IISMAXTIME
#define XPRS_MPSBOUNDNAME
Definition environment.h:99
#define XPRS_DUALSTRATEGY
#define XPRS_BAROBJPERTURB
#define XPRS_MIPFRACREDUCE
#define XPRS_TREEGOMCUTS
#define XPRS_OBJSCALEFACTOR
#define XPRS_FREE_SUPER
#define XPRS_TYPE_INT64
Definition environment.h:83
#define XPRS_SOLTIMELIMIT
#define XPRS_CROSSOVER
#define XPRS_USERSOLHEURISTIC
#define XPRS_BARFAILITERLIMIT
#define XPRS_PRECLIQUESTRATEGY
#define XPRS_FEASTOLTARGET
#define XPRS_BARCRASH
#define XPRS_MIPRELGAPNOTIFY
#define XPRSint64
Definition environment.h:54
#define XPRS_MIPRELSTOP
#define XPRS_HISTORYCOSTS
#define XPRS_HEURSEARCHROOTCUTFREQ
#define XPRS_LPLOGSTYLE
#define XPRS_BAROUTPUT
#define XPRS_MIPTERMINATIONMETHOD
#define XPRS_MAXSTALLTIME
#define XPRS_SOSREFTOL
#define XPRS_BACKTRACK
#define XPRS_BRANCHSTRUCTURAL
#define XPRS_PRICINGALG
struct xo_prob_struct * XPRSprob
Initial version of this code was provided by RTE.
Definition environment.h:26
#define XPRS_HEURSEARCHROOTSELECT
#define XPRS_MIPDUALREDUCTIONS
#define XPRS_PRECOEFELIM
#define XPRS_PREELIMQUAD
#define XPRS_FEASTOL
#define XPRS_MAXMIPSOL
#define XPRS_DUALPERTURB
#define XPRS_BARNUMSTABILITY
#define XPRS_PRESOLVEPASSES
#define XPRS_DETLOGFREQ
#define XPRS_CHOLESKYTOL
#define XPRS_BRANCHDISJ
#define XPRS_MAXTIME
#define XPRS_HEURFREQ
#define XPRS_IGNORECONTAINERMEMORYLIMIT
#define XPRS_PIVOTTOL
#define XPRS_PRIMALUNSHIFT
#define XPRS_MPSFORMAT
#define XPRS_CONCURRENTTHREADS
#define XPRS_HEURSEARCHEFFORT
#define XPRS_MIQCPALG
#define XPRS_MAXIMPLIEDBOUND
#define XPRS_PREPROBING
#define XPRS_OUTPUTTOL
#define XPRS_SBITERLIMIT
#define XPRS_PRESOLVE
#define XPRS_TREEQCCUTS
#define XPRS_ROOTPRESOLVE
#define XPRS_MPSECHO
#define XPRS_CROSSOVERTHREADS
#define XPRS_GOMCUTS
#define XPRS_BESTBOUND
#define XPRS_AUTOPERTURB
#define XPRS_PREBNDREDCONE
#define XPRS_MAXCHECKSONMAXCUTTIME
#define XPRS_VERSION
#define XPRS_MUTEXCALLBACKS
#define XPRS_CACHESIZE
#define XPRS_CHOLESKYALG
#define XPRS_BARSTART
#define XPRS_INDLINBIGM
#define XPRS_PRESORT
#define XPRS_LPLOG
#define XPRS_SYMSELECT
#define XPRS_AT_LOWER
#define XPRS_ELIMFILLIN
#define XPRS_SBESTIMATE
#define XPRS_HEURSELECT
#define XPRS_MAXIIS
#define XPRS_CORESPERCPU
#define XPRS_SIFTSWITCH
#define XPRS_MIPRELCUTOFF
#define XPRS_MPS18COMPATIBLE
#define XPRS_BARREFITER
#define XPRS_CUTSELECT
#define XPRS_BARSTARTWEIGHT
#define XPRS_OPTIMALITYTOLTARGET
#define XPRS_PPFACTOR
#define XPRS_TYPE_DOUBLE
Definition environment.h:84
#define XPRS_PREANALYTICCENTER
#define XPRS_IOTIMEOUT
#define XPRS_HEURDIVESTRATEGY
#define XPRS_BREADTHFIRST
#define XPRS_CPUTIME
#define XPRS_MIPRAMPUP
#define XPRS_SBBEST
#define XPRS_LOCALBACKTRACK
#define XPRS_RELPIVOTTOL
#define XPRS_MIPPRESOLVE
#define XPRS_TUNERMAXTIME
#define XPRS_FEASIBILITYPUMP
#define XPRS_TUNERMODE
#define XPRS_MIPOBJVAL
#define XPRS_PREDOMROW
#define XPRS_REPAIRINDEFINITEQMAX
#define XPRS_TUNERHISTORY
#define XPRS_REPAIRINDEFINITEQ
#define XPRS_BARKERNEL
#define XPRS_TREEDIAGNOSTICS
#define XPRS_PRECONFIGURATION
#define XPRS_CROSSOVEROPS
#define XPRS_ETATOL
#define XPRS_MIPABSGAPNOTIFYBOUND
#define XPRS_COMPUTEMATX_IIS
#define XPRS_OUTPUTLOG
#define XPRS_ELIMTOL
#define XPRS_NODES
#define XPRS_LP_OPTIMAL
#define XPRS_CONFLICTCUTS
#define XPRS_INDPRELINBIGM
#define XPRS_LPFLAGS
#define XPRS_PREDUPROW
#define XPRS_COVERCUTS
#define XPRS_QCCUTS
#define XPRS_PRIMALPERTURB
#define XPRS_ALGAFTERNETWORK
#define XPRS_DUALGRADIENT
#define XPRS_PENALTY
#define XPRS_AUTOCUTTING
#define XPRS_COMPUTELOG
#define XPRS_PRECOMPONENTS
#define XPRS_NODEPROBINGEFFORT
#define XPRS_PWLNONCONVEXTRANSFORMATION
#define XPRS_TREECOMPRESSION
#define XPRS_THREADS
#define XPRS_SYMMETRY
#define XPRS_FORCEOUTPUT
#define XPRS_TUNERMETHOD
#define XPRS_BARSTEPSTOP
#define XPRS_CUTSTRATEGY
#define XPRS_REFACTOR
#define XPRS_AUTOSCALING
#define XPRS_BARALG
#define XPRS_PRESOLVEMAXGROW
#define XPRS_TREEMEMORYLIMIT
#define XPRS_CUTFREQ
#define XPRS_MIPCOMPONENTS
#define XPRS_LPITERLIMIT
#define XPRS_TUNERSESSIONNAME
#define XPRS_BARORDER
#define XPRS_BARFREESCALE
#define XPRS_TUNEROUTPUTPATH
#define XPRS_MARKOWITZTOL
#define XPRS_HEURDIVERANDOMIZE
#define XPRS_PWLDUALREDUCTIONS
#define XPRS_AT_UPPER
#define XPRS_OUTPUTMASK
#define XPRS_DUALIZEOPS
#define XPRS_BARLARGEBOUND
#define XPRS_OPTIMALITYTOL
#define XPRS_KEEPNROWS
#define XPRS_MAXSCALEFACTOR
#define XPRS_CROSSOVERACCURACYTOL
#define XPRS_PRECONEDECOMP
#define XPRS_DUALTHREADS
#define XPRS_HEUREMPHASIS
#define XPRS_BARITERLIMIT
#define XPRS_KEEPBASIS
#define XPRS_BIGMMETHOD
#define XPRS_TUNERVERBOSE
#define XPRS_TUNERPERMUTE
#define XPRS_PREOBJCUTDETECT
#define XPRS_LOCALCHOICE
#define XPRS_LNPBEST
#define XPRS_PREPROTECTDUAL
#define XPRS_REPAIRINFEASTIMELIMIT
#define XPRS_MPSNAMELENGTH
#define XPRS_LPFOLDING
#define XPRS_GENCONSDUALREDUCTIONS
#define XPRS_LP_UNBOUNDED
#define XPRS_MATRIXTOL
#define XPRS_GENCONSABSTRANSFORMATION
#define XPRS_LPREFINEITERLIMIT
#define XPRS_PREDOMCOL
#define XPRS_HEURDIVESOFTROUNDING
#define XPRS_TUNEROUTPUT
#define XPRS_MIPREFINEITERLIMIT
#define XPRS_BARPERTURB
#define XPRS_REPAIRINFEASMAXTIME
#define XPRS_MAXTREEFILESIZE
#define XPRS_MIPRESTART
#define XPRS_HEURBEFORELP
#define XPRS_STOP_USER
Definition environment.h:74
#define XPRS_CALLBACKCHECKTIMEDELAY
#define XPRS_DEFAULTALG
#define XPRS_PRELINDEP
#define XPRS_EXTRASETS
#define XPRS_TIMELIMIT
#define XPRS_BARRHSSCALE
#define XPRS_LNPITERLIMIT
#define XPRS_BARREGULARIZE
#define XPRS_NODESELECTION
#define XPRS_BIGM
#define XPRS_MIPLOG
#define XPRS_SIFTPASSES
#define XPRS_IGNORECONTAINERCPULIMIT
#define XPRS_CALLBACKFROMMASTERTHREAD
#define XPRS_LPOBJVAL
#define XPRS_EXTRAROWS
#define XPRS_EXTRACOLS
#define XPRS_CUTFACTOR
#define XPRS_ESCAPENAMES
#define XPRS_MAXNODE
#define XPRS_MIPCONCURRENTNODES
#define XPRS_SIFTING
#define XPRS_EXTRASETELEMS
#define XPRS_EXTRAELEMS
#define XPRS_CUTDEPTH
#define XPRS_MIPABSCUTOFF
#define XPRS_TREECOVERCUTS
#define XPRS_DENSECOLLIMIT
#define XPRS_SLEEPONTHREADWAIT
#define XPRS_GLOBALSPATIALBRANCHIFPREFERORIG
#define XPRS_NAMES_COLUMN
Definition environment.h:90
#define XPRS_FORCEPARALLELDUAL
#define XPRS_MIP_INFEAS
#define XPRS_MIPCONCURRENTSOLVES
#define XPRS_MPSRHSNAME
Definition environment.h:96
#define XPRS_CROSSOVERDRP
#define XPRS_MULTIOBJOPS
#define XPRS_MIPRESTARTGAPTHRESHOLD
#define XPRS_MINUSINFINITY
Definition environment.h:93
#define XPRS_MULTIOBJLOG
#define XPRS_PSEUDOCOST
#define XPRS_MAXCUTTIME
#define XPRS_PREIMPLICATIONS
#define XPRS_PREFOLDING
#define XPRS_MPSRANGENAME
Definition environment.h:98
#define XPRS_MAXMCOEFFBUFFERELEMS
#define XPRS_BARTHREADS
#define XPRS_LUPIVOTTOL
#define XPRS_DUALIZE
#define XPRS_DETERMINISTIC
#define XPRS_MIPTOLTARGET
#define XPRS_SBSELECT
#define XPRS_MAXMEMORYSOFT
#define XPRS_TUNERMETHODFILE
#define XPRS_HEURTHREADS
#define XPRS_PREPERMUTESEED
#define XPRS_CC
Definition environment.h:60
#define XPRS_TUNERTARGET
#define XPRS_OBJ_MINIMIZE
#define XPRS_LPLOGDELAY
#define XPRS_NETSTALLLIMIT
#define XPRS_PREBNDREDQUAD
#define XPRS_BARCORES
#define XPRS_MIPTHREADS
#define XPRS_BARPRIMALSTOP
#define XPRS_NAN
#define setParamIfPossible_MACRO(target_map, setter, converter)
#define XPRS_CONTINUOUS
#define XPRS_INTEGER
Initial version of this code was provided by RTE.