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