Google OR-Tools v9.15
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 2010-2025 Google LLC
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 <numeric>
24#include <string>
25
26#include "absl/strings/numbers.h"
27#include "absl/strings/str_format.h"
29#include "ortools/base/timer.h"
32
33#define XPRS_INTEGER 'I'
34#define XPRS_CONTINUOUS 'C'
35
36// The argument to this macro is the invocation of a XPRS function that
37// returns a status. If the function returns non-zero the macro aborts
38// the program with an appropriate error message.
39#define CHECK_STATUS(s) \
40 do { \
41 int const status_ = s; \
42 CHECK_EQ(0, status_); \
43 } while (0)
44
45namespace operations_research {
46std::string getSolverVersion(XPRSprob const& prob) {
47 // XPRS_VERSION gives the version number as MAJOR*100 + RELEASE.
48 // It does not include the build number.
49 int version;
50 if (!prob || XPRSgetintcontrol(prob, XPRS_VERSION, &version))
51 return "XPRESS library version unknown";
52
53 int const major = version / 100;
54 version -= major * 100;
55 int const release = version;
56
57 return absl::StrFormat("XPRESS library version %d.%02d", major, release);
58}
59
60// Apply the specified name=value setting to prob.
61bool readParameter(XPRSprob const& prob, std::string const& name,
62 std::string const& value) {
63 // We cannot set empty parameters.
64 if (!value.size()) {
65 LOG(DFATAL) << "Empty value for parameter '" << name << "' in "
66 << getSolverVersion(prob);
67 return false;
68 }
69
70 // Figure out the type of the control.
71 int id, type;
72 if (XPRSgetcontrolinfo(prob, name.c_str(), &id, &type) ||
73 type == XPRS_TYPE_NOTDEFINED) {
74 LOG(DFATAL) << "Unknown parameter '" << name << "' in "
75 << getSolverVersion(prob);
76 return false;
77 }
78
79 // Depending on the type, parse the text in value and apply it.
80 std::stringstream v(value);
81 v.imbue(std::locale("C"));
82 switch (type) {
83 case XPRS_TYPE_INT: {
84 int i;
85 v >> i;
86 if (!v.eof()) {
87 LOG(DFATAL) << "Failed to parse value '" << value
88 << "' for int parameter '" << name << "' in "
89 << getSolverVersion(prob);
90 return false;
91 }
92 if (XPRSsetintcontrol(prob, id, i)) {
93 LOG(DFATAL) << "Failed to set int parameter '" << name << "' to "
94 << value << " (" << i << ") in " << getSolverVersion(prob);
95 return false;
96 }
97 } break;
98 case XPRS_TYPE_INT64: {
99 XPRSint64 i;
100 v >> i;
101 if (!v.eof()) {
102 LOG(DFATAL) << "Failed to parse value '" << value
103 << "' for int64_t parameter '" << name << "' in "
104 << getSolverVersion(prob);
105 return false;
106 }
107 if (XPRSsetintcontrol64(prob, id, i)) {
108 LOG(DFATAL) << "Failed to set int64_t parameter '" << name << "' to "
109 << value << " (" << i << ") in " << getSolverVersion(prob);
110 return false;
111 }
112 } break;
113 case XPRS_TYPE_DOUBLE: {
114 double d;
115 v >> d;
116 if (!v.eof()) {
117 LOG(DFATAL) << "Failed to parse value '" << value
118 << "' for dbl parameter '" << name << "' in "
119 << getSolverVersion(prob);
120 return false;
121 }
122 if (XPRSsetdblcontrol(prob, id, d)) {
123 LOG(DFATAL) << "Failed to set double parameter '" << name << "' to "
124 << value << " (" << d << ") in " << getSolverVersion(prob);
125 return false;
126 }
127 } break;
128 default:
129 // Note that string parameters are not supported at the moment since
130 // we don't want to deal with potential encoding or escaping issues.
131 LOG(DFATAL) << "Unsupported parameter type " << type << " for parameter '"
132 << name << "' in " << getSolverVersion(prob);
133 return false;
134 }
135
136 return true;
137}
138
139void printError(const XPRSprob& mLp, int line) {
140 char errmsg[512];
141 XPRSgetlasterror(mLp, errmsg);
142 VLOG(0) << absl::StrFormat("Function line %d did not execute correctly: %s\n",
143 line, errmsg);
144 exit(0);
145}
146
147void XPRS_CC XpressIntSolCallbackImpl(XPRSprob cbprob, void* cbdata);
148
149/**********************************************************************************\
150* Name: optimizermsg *
151* Purpose: Display Optimizer error messages and warnings. *
152* Arguments: const char *sMsg Message string *
153* int nLen Message length *
154* int nMsgLvl Message type *
155* Return Value: None *
156\**********************************************************************************/
157void XPRS_CC optimizermsg(XPRSprob prob, void* data, const char* sMsg, int nLen,
158 int nMsgLvl);
159
160int getnumcols(const XPRSprob& mLp) {
161 int nCols = 0;
162 XPRSgetintattrib(mLp, XPRS_COLS, &nCols);
163 return nCols;
164}
165
166int getnumrows(const XPRSprob& mLp) {
167 int nRows = 0;
168 XPRSgetintattrib(mLp, XPRS_ROWS, &nRows);
169 return nRows;
170}
171
172int getitcnt(const XPRSprob& mLp) {
173 int nIters = 0;
174 XPRSgetintattrib(mLp, XPRS_SIMPLEXITER, &nIters);
175 return nIters;
176}
177
178int getnodecnt(const XPRSprob& mLp) {
179 int nNodes = 0;
180 XPRSgetintattrib(mLp, XPRS_NODES, &nNodes);
181 return nNodes;
182}
183
184int setobjoffset(const XPRSprob& mLp, double value) {
185 // TODO detect xpress version
186 static int indexes[1] = {-1};
187 double values[1] = {-value};
188 XPRSchgobj(mLp, 1, indexes, values);
189 return 0;
190}
191
192void addhint(const XPRSprob& mLp, int length, const double solval[],
193 const int colind[]) {
194 // The OR-Tools API does not allow setting a name for the solution
195 // passing NULL to XPRESS will have it generate a unique ID for the solution
196 if (int status = XPRSaddmipsol(mLp, length, solval, colind, NULL)) {
197 LOG(WARNING) << "Failed to set solution hint.";
198 }
199}
200
202
204 // Reason values below 1000 are reserved by XPRESS
205 XPRSinterrupt(xprsProb, 1000 + reason);
206}
207
208// In case we need to return a double but don't have a value for that
209// we just return a NaN.
210#if !defined(XPRS_NAN)
211#define XPRS_NAN std::numeric_limits<double>::quiet_NaN()
212#endif
213
214using std::unique_ptr;
215
217 friend class XpressInterface;
218
219 public:
221 int num_nodes)
222 : xprsprob_(xprsprob),
223 event_(event),
224 num_nodes_(num_nodes),
225 variable_values_(0) {};
226
227 // Implementation of the interface.
228 MPCallbackEvent Event() override { return event_; };
229 bool CanQueryVariableValues() override;
230 double VariableValue(const MPVariable* variable) override;
231 void AddCut(const LinearRange& cutting_plane) override {
232 LOG(WARNING) << "AddCut is not implemented yet in XPRESS interface";
233 };
234 void AddLazyConstraint(const LinearRange& lazy_constraint) override {
235 LOG(WARNING) << "AddLazyConstraint inside Callback is not implemented yet "
236 "in XPRESS interface";
237 };
238 double SuggestSolution(
239 const absl::flat_hash_map<const MPVariable*, double>& solution) override;
240 int64_t NumExploredNodes() override { return num_nodes_; };
241
242 // Call this method to update the internal state of the callback context
243 // before passing it to MPCallback::RunCallback().
244 // Returns true if the internal state has changed.
246
247 private:
248 XPRSprob* xprsprob_;
249 MPCallbackEvent event_;
250 std::vector<double>
251 variable_values_; // same order as MPVariable* elements in MPSolver
252 int num_nodes_;
253};
254
255// Wraps the MPCallback in order to catch and store exceptions
257 public:
258 explicit MPCallbackWrapper(MPCallback* callback) : callback_(callback) {};
259 MPCallback* GetCallback() const { return callback_; }
260 // Since our (C++) call-back functions are called from the XPRESS (C) code,
261 // exceptions thrown in our call-back code are not caught by XPRESS.
262 // We have to catch them, interrupt XPRESS, and log them after XPRESS is
263 // effectively interrupted (ie after solve).
265 exceptions_mutex_.lock();
266 caught_exceptions_.push_back(std::current_exception());
268 exceptions_mutex_.unlock();
269 }
271 exceptions_mutex_.lock();
272 for (const std::exception_ptr& ex : caught_exceptions_) {
273 try {
274 std::rethrow_exception(ex);
275 } catch (std::exception& ex) {
276 // We don't want the interface to throw exceptions, plus it causes
277 // SWIG issues in Java & Python. Instead, we'll only log them.
278 // (The use cases where the user has to raise an exception inside their
279 // call-back does not seem to be frequent, anyway.)
280 LOG(ERROR) << "Caught exception during user-defined call-back: "
281 << ex.what();
282 }
283 }
284 caught_exceptions_.clear();
285 exceptions_mutex_.unlock();
286 };
287
288 private:
289 MPCallback* callback_;
290 std::vector<std::exception_ptr> caught_exceptions_;
291 std::mutex exceptions_mutex_;
292};
293
294// For a model that is extracted to an instance of this class there is a
295// 1:1 correspondence between MPVariable instances and XPRESS columns: the
296// index of an extracted variable is the column index in the XPRESS model.
297// Similar for instances of MPConstraint: the index of the constraint in
298// the model is the row index in the XPRESS model.
300 public:
301 // NOTE: 'mip' specifies the type of the problem (either continuous or
302 // mixed integer). This type is fixed for the lifetime of the
303 // instance. There are no dynamic changes to the model type.
304 explicit XpressInterface(MPSolver* solver, bool mip);
305 ~XpressInterface() override;
306
307 // Sets the optimization direction (min/max).
308 void SetOptimizationDirection(bool maximize) override;
309
310 // ----- Solve -----
311 // Solve the problem using the parameter values specified.
312 MPSolver::ResultStatus Solve(MPSolverParameters const& param) override;
313
314 // Writes the model.
315 void Write(const std::string& filename) override;
316
317 // ----- Model modifications and extraction -----
318 // Resets extracted model
319 void Reset() override;
320
321 void SetVariableBounds(int var_index, double lb, double ub) override;
322 void SetVariableInteger(int var_index, bool integer) override;
323 void SetConstraintBounds(int row_index, double lb, double ub) override;
324
325 void AddRowConstraint(MPConstraint* ct) override;
326 void AddVariable(MPVariable* var) override;
327 void SetCoefficient(MPConstraint* constraint, MPVariable const* variable,
328 double new_value, double old_value) override;
329
330 // Clear a constraint from all its terms.
331 void ClearConstraint(MPConstraint* constraint) override;
332 // Change a coefficient in the linear objective
333 void SetObjectiveCoefficient(MPVariable const* variable,
334 double coefficient) override;
335 // Change the constant term in the linear objective.
336 void SetObjectiveOffset(double value) override;
337 // Clear the objective from all its terms.
338 void ClearObjective() override;
339
340 // ------ Query statistics on the solution and the solve ------
341 // Number of simplex iterations
342 virtual int64_t iterations() const;
343 // Number of branch-and-bound nodes. Only available for discrete problems.
344 virtual int64_t nodes() const;
345
346 // Returns the basis status of a row.
347 MPSolver::BasisStatus row_status(int constraint_index) const override;
348 // Returns the basis status of a column.
349 MPSolver::BasisStatus column_status(int variable_index) const override;
350
351 // ----- Misc -----
352
353 // Query problem type.
354 // Remember that problem type is a static property that is set
355 // in the constructor and never changed.
356 bool IsContinuous() const override { return IsLP(); }
357 bool IsLP() const override { return !mMip; }
358 bool IsMIP() const override { return mMip; }
359
361 const std::vector<MPSolver::BasisStatus>& variable_statuses,
362 const std::vector<MPSolver::BasisStatus>& constraint_statuses) override;
363
364 void ExtractNewVariables() override;
365 void ExtractNewConstraints() override;
366 void ExtractObjective() override;
367
368 std::string SolverVersion() const override;
369
370 void* underlying_solver() override { return reinterpret_cast<void*>(mLp); }
371
372 double ComputeExactConditionNumber() const override {
373 if (!IsContinuous()) {
374 LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
375 << " XPRESS_MIXED_INTEGER_PROGRAMMING";
376 return 0.0;
377 }
378
379 // TODO(user): Not yet working.
380 LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
381 << " XPRESS_LINEAR_PROGRAMMING";
382 return 0.0;
383 }
384
385 void SetCallback(MPCallback* mp_callback) override;
386 bool SupportsCallbacks() const override { return true; }
387
388 bool InterruptSolve() override {
389 if (mLp) XPRSinterrupt(mLp, XPRS_STOP_USER);
390 return true;
391 }
392
393 protected:
394 // Set all parameters in the underlying solver.
395 void SetParameters(MPSolverParameters const& param) override;
396 // Set each parameter in the underlying solver.
397 void SetRelativeMipGap(double value) override;
398 void SetPrimalTolerance(double value) override;
399 void SetDualTolerance(double value) override;
400 void SetPresolveMode(int value) override;
401 void SetScalingMode(int value) override;
402 void SetLpAlgorithm(int value) override;
403
404 virtual bool ReadParameterFile(std::string const& filename);
405 virtual std::string ValidFileExtensionForParameterFile() const;
406
407 private:
408 // Mark modeling object "out of sync". This implicitly invalidates
409 // solution information as well. It is the counterpart of
410 // MPSolverInterface::InvalidateSolutionSynchronization
411 void InvalidateModelSynchronization() {
412 mCstat.clear();
413 mRstat.clear();
415 }
416 // Adds a new feasible, infeasible or partial MIP solution for the problem to
417 // the Optimizer. The hint is read in the MPSolver where the user set it using
418 // SetHint()
419 void AddSolutionHintToOptimizer();
420
421 bool readParameters(std::istream& is, char sep);
422
423 private:
424 XPRSprob mLp;
425 bool const mMip;
426
427 // Looping on MPConstraint::coefficients_ yields non-reproducible results
428 // since is uses pointer addresses as keys, the value of which is
429 // non-deterministic, especially their order.
430 absl::btree_map<int, std::map<int, double> >
431 fixedOrderCoefficientsPerConstraint;
432
433 // Incremental extraction.
434 // Without incremental extraction we have to re-extract the model every
435 // time we perform a solve. Due to the way the Reset() function is
436 // implemented, this will lose MIP start or basis information from a
437 // previous solve. On the other hand, if there is a significant changes
438 // to the model then just re-extracting everything is usually faster than
439 // keeping the low-level modeling object in sync with the high-level
440 // variables/constraints.
441 // Note that incremental extraction is particularly expensive in function
442 // ExtractNewVariables() since there we must scan _all_ old constraints
443 // and update them with respect to the new variables.
444 bool const supportIncrementalExtraction;
445
446 // Use slow and immediate updates or try to do bulk updates.
447 // For many updates to the model we have the option to either perform
448 // the update immediately with a potentially slow operation or to
449 // just mark the low-level modeling object out of sync and re-extract
450 // the model later.
451 enum SlowUpdates {
452 SlowSetCoefficient = 0x0001,
453 SlowClearConstraint = 0x0002,
454 SlowSetObjectiveCoefficient = 0x0004,
455 SlowClearObjective = 0x0008,
456 SlowSetConstraintBounds = 0x0010,
457 SlowSetVariableInteger = 0x0020,
458 SlowSetVariableBounds = 0x0040,
459 SlowUpdatesAll = 0xffff
460 } const slowUpdates;
461 // XPRESS has no method to query the basis status of a single variable.
462 // Hence, we query the status only once and cache the array. This is
463 // much faster in case the basis status of more than one row/column
464 // is required.
465
466 // TODO
467 std::vector<int> mutable mCstat;
468 std::vector<int> mutable mRstat;
469
470 std::vector<int> mutable initial_variables_basis_status_;
471 std::vector<int> mutable initial_constraint_basis_status_;
472
473 // Setup the right-hand side of a constraint from its lower and upper bound.
474 static void MakeRhs(double lb, double ub, double& rhs, char& sense,
475 double& range);
476
477 std::map<std::string, int>& mapStringControls_;
478 std::map<std::string, int>& mapDoubleControls_;
479 std::map<std::string, int>& mapIntegerControls_;
480 std::map<std::string, int>& mapInteger64Controls_;
481
482 bool SetSolverSpecificParametersAsString(
483 const std::string& parameters) override;
484 MPCallback* callback_ = nullptr;
485};
486
487// Transform MPSolver basis status to XPRESS status
489 MPSolver::BasisStatus mpsolver_basis_status);
490// Transform XPRESS basis status to MPSolver basis status.
492 int xpress_basis_status);
493
494static std::map<std::string, int>& getMapStringControls() {
495 static std::map<std::string, int> mapControls = {
496 {"MPSRHSNAME", XPRS_MPSRHSNAME},
497 {"MPSOBJNAME", XPRS_MPSOBJNAME},
498 {"MPSRANGENAME", XPRS_MPSRANGENAME},
499 {"MPSBOUNDNAME", XPRS_MPSBOUNDNAME},
500 {"OUTPUTMASK", XPRS_OUTPUTMASK},
501 {"TUNERMETHODFILE", XPRS_TUNERMETHODFILE},
502 {"TUNEROUTPUTPATH", XPRS_TUNEROUTPUTPATH},
503 {"TUNERSESSIONNAME", XPRS_TUNERSESSIONNAME},
504 {"COMPUTEEXECSERVICE", XPRS_COMPUTEEXECSERVICE},
505 };
506 return mapControls;
507}
508
509static std::map<std::string, int>& getMapDoubleControls() {
510 static std::map<std::string, int> mapControls = {
511 {"MAXCUTTIME", XPRS_MAXCUTTIME},
512 {"MAXSTALLTIME", XPRS_MAXSTALLTIME},
513 {"TUNERMAXTIME", XPRS_TUNERMAXTIME},
514 {"MATRIXTOL", XPRS_MATRIXTOL},
515 {"PIVOTTOL", XPRS_PIVOTTOL},
516 {"FEASTOL", XPRS_FEASTOL},
517 {"OUTPUTTOL", XPRS_OUTPUTTOL},
518 {"SOSREFTOL", XPRS_SOSREFTOL},
519 {"OPTIMALITYTOL", XPRS_OPTIMALITYTOL},
520 {"ETATOL", XPRS_ETATOL},
521 {"RELPIVOTTOL", XPRS_RELPIVOTTOL},
522 {"MIPTOL", XPRS_MIPTOL},
523 {"MIPTOLTARGET", XPRS_MIPTOLTARGET},
524 {"BARPERTURB", XPRS_BARPERTURB},
525 {"MIPADDCUTOFF", XPRS_MIPADDCUTOFF},
526 {"MIPABSCUTOFF", XPRS_MIPABSCUTOFF},
527 {"MIPRELCUTOFF", XPRS_MIPRELCUTOFF},
528 {"PSEUDOCOST", XPRS_PSEUDOCOST},
529 {"PENALTY", XPRS_PENALTY},
530 {"BIGM", XPRS_BIGM},
531 {"MIPABSSTOP", XPRS_MIPABSSTOP},
532 {"MIPRELSTOP", XPRS_MIPRELSTOP},
533 {"CROSSOVERACCURACYTOL", XPRS_CROSSOVERACCURACYTOL},
534 {"PRIMALPERTURB", XPRS_PRIMALPERTURB},
535 {"DUALPERTURB", XPRS_DUALPERTURB},
536 {"BAROBJSCALE", XPRS_BAROBJSCALE},
537 {"BARRHSSCALE", XPRS_BARRHSSCALE},
538 {"CHOLESKYTOL", XPRS_CHOLESKYTOL},
539 {"BARGAPSTOP", XPRS_BARGAPSTOP},
540 {"BARDUALSTOP", XPRS_BARDUALSTOP},
541 {"BARPRIMALSTOP", XPRS_BARPRIMALSTOP},
542 {"BARSTEPSTOP", XPRS_BARSTEPSTOP},
543 {"ELIMTOL", XPRS_ELIMTOL},
544 {"MARKOWITZTOL", XPRS_MARKOWITZTOL},
545 {"MIPABSGAPNOTIFY", XPRS_MIPABSGAPNOTIFY},
546 {"MIPRELGAPNOTIFY", XPRS_MIPRELGAPNOTIFY},
547 {"BARLARGEBOUND", XPRS_BARLARGEBOUND},
548 {"PPFACTOR", XPRS_PPFACTOR},
549 {"REPAIRINDEFINITEQMAX", XPRS_REPAIRINDEFINITEQMAX},
550 {"BARGAPTARGET", XPRS_BARGAPTARGET},
551 {"DUMMYCONTROL", XPRS_DUMMYCONTROL},
552 {"BARSTARTWEIGHT", XPRS_BARSTARTWEIGHT},
553 {"BARFREESCALE", XPRS_BARFREESCALE},
554 {"SBEFFORT", XPRS_SBEFFORT},
555 {"HEURDIVERANDOMIZE", XPRS_HEURDIVERANDOMIZE},
556 {"HEURSEARCHEFFORT", XPRS_HEURSEARCHEFFORT},
557 {"CUTFACTOR", XPRS_CUTFACTOR},
558 {"EIGENVALUETOL", XPRS_EIGENVALUETOL},
559 {"INDLINBIGM", XPRS_INDLINBIGM},
560 {"TREEMEMORYSAVINGTARGET", XPRS_TREEMEMORYSAVINGTARGET},
561 {"INDPRELINBIGM", XPRS_INDPRELINBIGM},
562 {"RELAXTREEMEMORYLIMIT", XPRS_RELAXTREEMEMORYLIMIT},
563 {"MIPABSGAPNOTIFYOBJ", XPRS_MIPABSGAPNOTIFYOBJ},
564 {"MIPABSGAPNOTIFYBOUND", XPRS_MIPABSGAPNOTIFYBOUND},
565 {"PRESOLVEMAXGROW", XPRS_PRESOLVEMAXGROW},
566 {"HEURSEARCHTARGETSIZE", XPRS_HEURSEARCHTARGETSIZE},
567 {"CROSSOVERRELPIVOTTOL", XPRS_CROSSOVERRELPIVOTTOL},
568 {"CROSSOVERRELPIVOTTOLSAFE", XPRS_CROSSOVERRELPIVOTTOLSAFE},
569 {"DETLOGFREQ", XPRS_DETLOGFREQ},
570 {"MAXIMPLIEDBOUND", XPRS_MAXIMPLIEDBOUND},
571 {"FEASTOLTARGET", XPRS_FEASTOLTARGET},
572 {"OPTIMALITYTOLTARGET", XPRS_OPTIMALITYTOLTARGET},
573 {"PRECOMPONENTSEFFORT", XPRS_PRECOMPONENTSEFFORT},
574 {"LPLOGDELAY", XPRS_LPLOGDELAY},
575 {"HEURDIVEITERLIMIT", XPRS_HEURDIVEITERLIMIT},
576 {"BARKERNEL", XPRS_BARKERNEL},
577 {"FEASTOLPERTURB", XPRS_FEASTOLPERTURB},
578 {"CROSSOVERFEASWEIGHT", XPRS_CROSSOVERFEASWEIGHT},
579 {"LUPIVOTTOL", XPRS_LUPIVOTTOL},
580 {"MIPRESTARTGAPTHRESHOLD", XPRS_MIPRESTARTGAPTHRESHOLD},
581 {"NODEPROBINGEFFORT", XPRS_NODEPROBINGEFFORT},
582 {"INPUTTOL", XPRS_INPUTTOL},
583 {"MIPRESTARTFACTOR", XPRS_MIPRESTARTFACTOR},
584 {"BAROBJPERTURB", XPRS_BAROBJPERTURB},
585 {"CPIALPHA", XPRS_CPIALPHA},
586 {"GLOBALBOUNDINGBOX", XPRS_GLOBALBOUNDINGBOX},
587 {"TIMELIMIT", XPRS_TIMELIMIT},
588 {"SOLTIMELIMIT", XPRS_SOLTIMELIMIT},
589 {"REPAIRINFEASTIMELIMIT", XPRS_REPAIRINFEASTIMELIMIT},
590 };
591 return mapControls;
592}
593
594static std::map<std::string, int>& getMapIntControls() {
595 static std::map<std::string, int> mapControls = {
596 {"EXTRAROWS", XPRS_EXTRAROWS},
597 {"EXTRACOLS", XPRS_EXTRACOLS},
598 {"LPITERLIMIT", XPRS_LPITERLIMIT},
599 {"LPLOG", XPRS_LPLOG},
600 {"SCALING", XPRS_SCALING},
601 {"PRESOLVE", XPRS_PRESOLVE},
602 {"CRASH", XPRS_CRASH},
603 {"PRICINGALG", XPRS_PRICINGALG},
604 {"INVERTFREQ", XPRS_INVERTFREQ},
605 {"INVERTMIN", XPRS_INVERTMIN},
606 {"MAXNODE", XPRS_MAXNODE},
607 {"MAXTIME", XPRS_MAXTIME},
608 {"MAXMIPSOL", XPRS_MAXMIPSOL},
609 {"SIFTPASSES", XPRS_SIFTPASSES},
610 {"DEFAULTALG", XPRS_DEFAULTALG},
611 {"VARSELECTION", XPRS_VARSELECTION},
612 {"NODESELECTION", XPRS_NODESELECTION},
613 {"BACKTRACK", XPRS_BACKTRACK},
614 {"MIPLOG", XPRS_MIPLOG},
615 {"KEEPNROWS", XPRS_KEEPNROWS},
616 {"MPSECHO", XPRS_MPSECHO},
617 {"MAXPAGELINES", XPRS_MAXPAGELINES},
618 {"OUTPUTLOG", XPRS_OUTPUTLOG},
619 {"BARSOLUTION", XPRS_BARSOLUTION},
620 {"CACHESIZE", XPRS_CACHESIZE},
621 {"CROSSOVER", XPRS_CROSSOVER},
622 {"BARITERLIMIT", XPRS_BARITERLIMIT},
623 {"CHOLESKYALG", XPRS_CHOLESKYALG},
624 {"BAROUTPUT", XPRS_BAROUTPUT},
625 {"EXTRAMIPENTS", XPRS_EXTRAMIPENTS},
626 {"REFACTOR", XPRS_REFACTOR},
627 {"BARTHREADS", XPRS_BARTHREADS},
628 {"KEEPBASIS", XPRS_KEEPBASIS},
629 {"CROSSOVEROPS", XPRS_CROSSOVEROPS},
630 {"VERSION", XPRS_VERSION},
631 {"CROSSOVERTHREADS", XPRS_CROSSOVERTHREADS},
632 {"BIGMMETHOD", XPRS_BIGMMETHOD},
633 {"MPSNAMELENGTH", XPRS_MPSNAMELENGTH},
634 {"ELIMFILLIN", XPRS_ELIMFILLIN},
635 {"PRESOLVEOPS", XPRS_PRESOLVEOPS},
636 {"MIPPRESOLVE", XPRS_MIPPRESOLVE},
637 {"MIPTHREADS", XPRS_MIPTHREADS},
638 {"BARORDER", XPRS_BARORDER},
639 {"BREADTHFIRST", XPRS_BREADTHFIRST},
640 {"AUTOPERTURB", XPRS_AUTOPERTURB},
641 {"DENSECOLLIMIT", XPRS_DENSECOLLIMIT},
642 {"CALLBACKFROMMASTERTHREAD", XPRS_CALLBACKFROMMASTERTHREAD},
643 {"MAXMCOEFFBUFFERELEMS", XPRS_MAXMCOEFFBUFFERELEMS},
644 {"REFINEOPS", XPRS_REFINEOPS},
645 {"LPREFINEITERLIMIT", XPRS_LPREFINEITERLIMIT},
646 {"MIPREFINEITERLIMIT", XPRS_MIPREFINEITERLIMIT},
647 {"DUALIZEOPS", XPRS_DUALIZEOPS},
648 {"CROSSOVERITERLIMIT", XPRS_CROSSOVERITERLIMIT},
649 {"PREBASISRED", XPRS_PREBASISRED},
650 {"PRESORT", XPRS_PRESORT},
651 {"PREPERMUTE", XPRS_PREPERMUTE},
652 {"PREPERMUTESEED", XPRS_PREPERMUTESEED},
653 {"MAXMEMORYSOFT", XPRS_MAXMEMORYSOFT},
654 {"CUTFREQ", XPRS_CUTFREQ},
655 {"SYMSELECT", XPRS_SYMSELECT},
656 {"SYMMETRY", XPRS_SYMMETRY},
657 {"MAXMEMORYHARD", XPRS_MAXMEMORYHARD},
658 {"MIQCPALG", XPRS_MIQCPALG},
659 {"QCCUTS", XPRS_QCCUTS},
660 {"QCROOTALG", XPRS_QCROOTALG},
661 {"PRECONVERTSEPARABLE", XPRS_PRECONVERTSEPARABLE},
662 {"ALGAFTERNETWORK", XPRS_ALGAFTERNETWORK},
663 {"TRACE", XPRS_TRACE},
664 {"MAXIIS", XPRS_MAXIIS},
665 {"CPUTIME", XPRS_CPUTIME},
666 {"COVERCUTS", XPRS_COVERCUTS},
667 {"GOMCUTS", XPRS_GOMCUTS},
668 {"LPFOLDING", XPRS_LPFOLDING},
669 {"MPSFORMAT", XPRS_MPSFORMAT},
670 {"CUTSTRATEGY", XPRS_CUTSTRATEGY},
671 {"CUTDEPTH", XPRS_CUTDEPTH},
672 {"TREECOVERCUTS", XPRS_TREECOVERCUTS},
673 {"TREEGOMCUTS", XPRS_TREEGOMCUTS},
674 {"CUTSELECT", XPRS_CUTSELECT},
675 {"TREECUTSELECT", XPRS_TREECUTSELECT},
676 {"DUALIZE", XPRS_DUALIZE},
677 {"DUALGRADIENT", XPRS_DUALGRADIENT},
678 {"SBITERLIMIT", XPRS_SBITERLIMIT},
679 {"SBBEST", XPRS_SBBEST},
680 {"BARINDEFLIMIT", XPRS_BARINDEFLIMIT},
681 {"HEURFREQ", XPRS_HEURFREQ},
682 {"HEURDEPTH", XPRS_HEURDEPTH},
683 {"HEURMAXSOL", XPRS_HEURMAXSOL},
684 {"HEURNODES", XPRS_HEURNODES},
685 {"LNPBEST", XPRS_LNPBEST},
686 {"LNPITERLIMIT", XPRS_LNPITERLIMIT},
687 {"BRANCHCHOICE", XPRS_BRANCHCHOICE},
688 {"BARREGULARIZE", XPRS_BARREGULARIZE},
689 {"SBSELECT", XPRS_SBSELECT},
690 {"LOCALCHOICE", XPRS_LOCALCHOICE},
691 {"LOCALBACKTRACK", XPRS_LOCALBACKTRACK},
692 {"DUALSTRATEGY", XPRS_DUALSTRATEGY},
693 {"L1CACHE", XPRS_L1CACHE},
694 {"HEURDIVESTRATEGY", XPRS_HEURDIVESTRATEGY},
695 {"HEURSELECT", XPRS_HEURSELECT},
696 {"BARSTART", XPRS_BARSTART},
697 {"PRESOLVEPASSES", XPRS_PRESOLVEPASSES},
698 {"BARNUMSTABILITY", XPRS_BARNUMSTABILITY},
699 {"BARORDERTHREADS", XPRS_BARORDERTHREADS},
700 {"EXTRASETS", XPRS_EXTRASETS},
701 {"FEASIBILITYPUMP", XPRS_FEASIBILITYPUMP},
702 {"PRECOEFELIM", XPRS_PRECOEFELIM},
703 {"PREDOMCOL", XPRS_PREDOMCOL},
704 {"HEURSEARCHFREQ", XPRS_HEURSEARCHFREQ},
705 {"HEURDIVESPEEDUP", XPRS_HEURDIVESPEEDUP},
706 {"SBESTIMATE", XPRS_SBESTIMATE},
707 {"BARCORES", XPRS_BARCORES},
708 {"MAXCHECKSONMAXTIME", XPRS_MAXCHECKSONMAXTIME},
709 {"MAXCHECKSONMAXCUTTIME", XPRS_MAXCHECKSONMAXCUTTIME},
710 {"HISTORYCOSTS", XPRS_HISTORYCOSTS},
711 {"ALGAFTERCROSSOVER", XPRS_ALGAFTERCROSSOVER},
712 {"MUTEXCALLBACKS", XPRS_MUTEXCALLBACKS},
713 {"BARCRASH", XPRS_BARCRASH},
714 {"HEURDIVESOFTROUNDING", XPRS_HEURDIVESOFTROUNDING},
715 {"HEURSEARCHROOTSELECT", XPRS_HEURSEARCHROOTSELECT},
716 {"HEURSEARCHTREESELECT", XPRS_HEURSEARCHTREESELECT},
717 {"MPS18COMPATIBLE", XPRS_MPS18COMPATIBLE},
718 {"ROOTPRESOLVE", XPRS_ROOTPRESOLVE},
719 {"CROSSOVERDRP", XPRS_CROSSOVERDRP},
720 {"FORCEOUTPUT", XPRS_FORCEOUTPUT},
721 {"PRIMALOPS", XPRS_PRIMALOPS},
722 {"DETERMINISTIC", XPRS_DETERMINISTIC},
723 {"PREPROBING", XPRS_PREPROBING},
724 {"TREEMEMORYLIMIT", XPRS_TREEMEMORYLIMIT},
725 {"TREECOMPRESSION", XPRS_TREECOMPRESSION},
726 {"TREEDIAGNOSTICS", XPRS_TREEDIAGNOSTICS},
727 {"MAXTREEFILESIZE", XPRS_MAXTREEFILESIZE},
728 {"PRECLIQUESTRATEGY", XPRS_PRECLIQUESTRATEGY},
729 {"REPAIRINFEASMAXTIME", XPRS_REPAIRINFEASMAXTIME},
730 {"IFCHECKCONVEXITY", XPRS_IFCHECKCONVEXITY},
731 {"PRIMALUNSHIFT", XPRS_PRIMALUNSHIFT},
732 {"REPAIRINDEFINITEQ", XPRS_REPAIRINDEFINITEQ},
733 {"MIPRAMPUP", XPRS_MIPRAMPUP},
734 {"MAXLOCALBACKTRACK", XPRS_MAXLOCALBACKTRACK},
735 {"USERSOLHEURISTIC", XPRS_USERSOLHEURISTIC},
736 {"FORCEPARALLELDUAL", XPRS_FORCEPARALLELDUAL},
737 {"BACKTRACKTIE", XPRS_BACKTRACKTIE},
738 {"BRANCHDISJ", XPRS_BRANCHDISJ},
739 {"MIPFRACREDUCE", XPRS_MIPFRACREDUCE},
740 {"CONCURRENTTHREADS", XPRS_CONCURRENTTHREADS},
741 {"MAXSCALEFACTOR", XPRS_MAXSCALEFACTOR},
742 {"HEURTHREADS", XPRS_HEURTHREADS},
743 {"THREADS", XPRS_THREADS},
744 {"HEURBEFORELP", XPRS_HEURBEFORELP},
745 {"PREDOMROW", XPRS_PREDOMROW},
746 {"BRANCHSTRUCTURAL", XPRS_BRANCHSTRUCTURAL},
747 {"QUADRATICUNSHIFT", XPRS_QUADRATICUNSHIFT},
748 {"BARPRESOLVEOPS", XPRS_BARPRESOLVEOPS},
749 {"QSIMPLEXOPS", XPRS_QSIMPLEXOPS},
750 {"MIPRESTART", XPRS_MIPRESTART},
751 {"CONFLICTCUTS", XPRS_CONFLICTCUTS},
752 {"PREPROTECTDUAL", XPRS_PREPROTECTDUAL},
753 {"CORESPERCPU", XPRS_CORESPERCPU},
754 {"RESOURCESTRATEGY", XPRS_RESOURCESTRATEGY},
755 {"CLAMPING", XPRS_CLAMPING},
756 {"SLEEPONTHREADWAIT", XPRS_SLEEPONTHREADWAIT},
757 {"PREDUPROW", XPRS_PREDUPROW},
758 {"CPUPLATFORM", XPRS_CPUPLATFORM},
759 {"BARALG", XPRS_BARALG},
760 {"SIFTING", XPRS_SIFTING},
761 {"LPLOGSTYLE", XPRS_LPLOGSTYLE},
762 {"RANDOMSEED", XPRS_RANDOMSEED},
763 {"TREEQCCUTS", XPRS_TREEQCCUTS},
764 {"PRELINDEP", XPRS_PRELINDEP},
765 {"DUALTHREADS", XPRS_DUALTHREADS},
766 {"PREOBJCUTDETECT", XPRS_PREOBJCUTDETECT},
767 {"PREBNDREDQUAD", XPRS_PREBNDREDQUAD},
768 {"PREBNDREDCONE", XPRS_PREBNDREDCONE},
769 {"PRECOMPONENTS", XPRS_PRECOMPONENTS},
770 {"MAXMIPTASKS", XPRS_MAXMIPTASKS},
771 {"MIPTERMINATIONMETHOD", XPRS_MIPTERMINATIONMETHOD},
772 {"PRECONEDECOMP", XPRS_PRECONEDECOMP},
773 {"HEURFORCESPECIALOBJ", XPRS_HEURFORCESPECIALOBJ},
774 {"HEURSEARCHROOTCUTFREQ", XPRS_HEURSEARCHROOTCUTFREQ},
775 {"PREELIMQUAD", XPRS_PREELIMQUAD},
776 {"PREIMPLICATIONS", XPRS_PREIMPLICATIONS},
777 {"TUNERMODE", XPRS_TUNERMODE},
778 {"TUNERMETHOD", XPRS_TUNERMETHOD},
779 {"TUNERTARGET", XPRS_TUNERTARGET},
780 {"TUNERTHREADS", XPRS_TUNERTHREADS},
781 {"TUNERHISTORY", XPRS_TUNERHISTORY},
782 {"TUNERPERMUTE", XPRS_TUNERPERMUTE},
783 {"TUNERVERBOSE", XPRS_TUNERVERBOSE},
784 {"TUNEROUTPUT", XPRS_TUNEROUTPUT},
785 {"PREANALYTICCENTER", XPRS_PREANALYTICCENTER},
786 {"NETCUTS", XPRS_NETCUTS},
787 {"LPFLAGS", XPRS_LPFLAGS},
788 {"MIPKAPPAFREQ", XPRS_MIPKAPPAFREQ},
789 {"OBJSCALEFACTOR", XPRS_OBJSCALEFACTOR},
790 {"TREEFILELOGINTERVAL", XPRS_TREEFILELOGINTERVAL},
791 {"IGNORECONTAINERCPULIMIT", XPRS_IGNORECONTAINERCPULIMIT},
792 {"IGNORECONTAINERMEMORYLIMIT", XPRS_IGNORECONTAINERMEMORYLIMIT},
793 {"MIPDUALREDUCTIONS", XPRS_MIPDUALREDUCTIONS},
794 {"GENCONSDUALREDUCTIONS", XPRS_GENCONSDUALREDUCTIONS},
795 {"PWLDUALREDUCTIONS", XPRS_PWLDUALREDUCTIONS},
796 {"BARFAILITERLIMIT", XPRS_BARFAILITERLIMIT},
797 {"AUTOSCALING", XPRS_AUTOSCALING},
798 {"GENCONSABSTRANSFORMATION", XPRS_GENCONSABSTRANSFORMATION},
799 {"COMPUTEJOBPRIORITY", XPRS_COMPUTEJOBPRIORITY},
800 {"PREFOLDING", XPRS_PREFOLDING},
801 {"NETSTALLLIMIT", XPRS_NETSTALLLIMIT},
802 {"SERIALIZEPREINTSOL", XPRS_SERIALIZEPREINTSOL},
803 {"NUMERICALEMPHASIS", XPRS_NUMERICALEMPHASIS},
804 {"PWLNONCONVEXTRANSFORMATION", XPRS_PWLNONCONVEXTRANSFORMATION},
805 {"MIPCOMPONENTS", XPRS_MIPCOMPONENTS},
806 {"MIPCONCURRENTNODES", XPRS_MIPCONCURRENTNODES},
807 {"MIPCONCURRENTSOLVES", XPRS_MIPCONCURRENTSOLVES},
808 {"OUTPUTCONTROLS", XPRS_OUTPUTCONTROLS},
809 {"SIFTSWITCH", XPRS_SIFTSWITCH},
810 {"HEUREMPHASIS", XPRS_HEUREMPHASIS},
811 {"COMPUTEMATX", XPRS_COMPUTEMATX},
812 {"COMPUTEMATX_IIS", XPRS_COMPUTEMATX_IIS},
813 {"COMPUTEMATX_IISMAXTIME", XPRS_COMPUTEMATX_IISMAXTIME},
814 {"BARREFITER", XPRS_BARREFITER},
815 {"COMPUTELOG", XPRS_COMPUTELOG},
816 {"SIFTPRESOLVEOPS", XPRS_SIFTPRESOLVEOPS},
817 {"CHECKINPUTDATA", XPRS_CHECKINPUTDATA},
818 {"ESCAPENAMES", XPRS_ESCAPENAMES},
819 {"IOTIMEOUT", XPRS_IOTIMEOUT},
820 {"AUTOCUTTING", XPRS_AUTOCUTTING},
821 {"CALLBACKCHECKTIMEDELAY", XPRS_CALLBACKCHECKTIMEDELAY},
822 {"MULTIOBJOPS", XPRS_MULTIOBJOPS},
823 {"MULTIOBJLOG", XPRS_MULTIOBJLOG},
824 {"GLOBALSPATIALBRANCHIFPREFERORIG", XPRS_GLOBALSPATIALBRANCHIFPREFERORIG},
825 {"PRECONFIGURATION", XPRS_PRECONFIGURATION},
826 {"FEASIBILITYJUMP", XPRS_FEASIBILITYJUMP},
827 };
828 return mapControls;
829}
830
831static std::map<std::string, int>& getMapInt64Controls() {
832 static std::map<std::string, int> mapControls = {
833 {"EXTRAELEMS", XPRS_EXTRAELEMS},
834 {"EXTRASETELEMS", XPRS_EXTRASETELEMS},
835 };
836 return mapControls;
837}
838
839// Creates an LP/MIP instance.
841 : MPSolverInterface(solver),
842 mLp(nullptr),
843 mMip(mip),
844 supportIncrementalExtraction(false),
845 slowUpdates(SlowClearObjective),
846 mapStringControls_(getMapStringControls()),
847 mapDoubleControls_(getMapDoubleControls()),
848 mapIntegerControls_(getMapIntControls()),
849 mapInteger64Controls_(getMapInt64Controls()) {
850 bool correctlyLoaded = initXpressEnv();
851 CHECK(correctlyLoaded);
852 int status = XPRScreateprob(&mLp);
853 CHECK_STATUS(status);
854 DCHECK(mLp != nullptr); // should not be NULL if status=0
855 int nReturn = XPRSaddcbmessage(mLp, optimizermsg, (void*)this, 0);
858}
859
864
866 // We prefer XPRSversionnumber() over XPRSversion() since the
867 // former will never pose any encoding issues.
868 int version = 0;
870
871 int const major = version / 1000000;
872 version -= major * 1000000;
873 int const release = version / 10000;
874 version -= release * 10000;
875 int const mod = version / 100;
876 version -= mod * 100;
877 int const fix = version;
878
879 return absl::StrFormat("XPRESS library version %d.%02d.%02d.%02d", major,
880 release, mod, fix);
881}
882
883// ------ Model modifications and extraction -----
884
886 int nRows = getnumrows(mLp);
887 std::vector<int> rows(nRows);
888 std::iota(rows.begin(), rows.end(), 0);
889 int nCols = getnumcols(mLp);
890 std::vector<int> cols(nCols);
891 std::iota(cols.begin(), cols.end(), 0);
892 XPRSdelrows(mLp, nRows, rows.data());
893 XPRSdelcols(mLp, nCols, cols.data());
894 XPRSdelobj(mLp, 0);
896 mCstat.clear();
897 mRstat.clear();
898}
899
904
905void XpressInterface::SetVariableBounds(int var_index, double lb, double ub) {
907
908 // Changing the bounds of a variable is fast. However, doing this for
909 // many variables may still be slow. So we don't perform the update by
910 // default. However, if we support incremental extraction
911 // (supportIncrementalExtraction is true) then we MUST perform the
912 // update here, or we will lose it.
913
914 if (!supportIncrementalExtraction && !(slowUpdates & SlowSetVariableBounds)) {
915 InvalidateModelSynchronization();
916 } else {
917 if (variable_is_extracted(var_index)) {
918 // Variable has already been extracted, so we must modify the
919 // modeling object.
920 DCHECK_LT(var_index, last_variable_index_);
921 char const lu[2] = {'L', 'U'};
922 double const bd[2] = {lb, ub};
923 int const idx[2] = {var_index, var_index};
924 CHECK_STATUS(XPRSchgbounds(mLp, 2, idx, lu, bd));
925 } else {
926 // Variable is not yet extracted. It is sufficient to just mark
927 // the modeling object "out of sync"
928 InvalidateModelSynchronization();
929 }
930 }
931}
932
933// Modifies integrality of an extracted variable.
934void XpressInterface::SetVariableInteger(int var_index, bool integer) {
936
937 // NOTE: The type of the model (continuous or mixed integer) is
938 // defined once and for all in the constructor. There are no
939 // dynamic changes to the model type.
940
941 // Changing the type of a variable should be fast. Still, doing all
942 // updates in one big chunk right before solve() is usually faster.
943 // However, if we support incremental extraction
944 // (supportIncrementalExtraction is true) then we MUST change the
945 // type of extracted variables here.
946
947 if (!supportIncrementalExtraction &&
948 !(slowUpdates & SlowSetVariableInteger)) {
949 InvalidateModelSynchronization();
950 } else {
951 if (mMip) {
952 if (variable_is_extracted(var_index)) {
953 // Variable is extracted. Change the type immediately.
954 // TODO: Should we check the current type and don't do anything
955 // in case the type does not change?
956 DCHECK_LE(var_index, getnumcols(mLp));
957 char const type = integer ? XPRS_INTEGER : XPRS_CONTINUOUS;
958 CHECK_STATUS(XPRSchgcoltype(mLp, 1, &var_index, &type));
959 } else {
960 InvalidateModelSynchronization();
961 }
962 } else {
963 LOG(DFATAL)
964 << "Attempt to change variable to integer in non-MIP problem!";
965 }
966 }
967}
968
969// Setup the right-hand side of a constraint.
970void XpressInterface::MakeRhs(double lb, double ub, double& rhs, char& sense,
971 double& range) {
972 if (lb == ub) {
973 // Both bounds are equal -> this is an equality constraint
974 rhs = lb;
975 range = 0.0;
976 sense = 'E';
977 } else if (lb > XPRS_MINUSINFINITY && ub < XPRS_PLUSINFINITY) {
978 // Both bounds are finite -> this is a ranged constraint
979 // The value of a ranged constraint is allowed to be in
980 // [ rhs-rngval, rhs ]
981 // Xpress does not support contradictory bounds. Instead the sign on
982 // rndval is always ignored.
983 if (lb > ub) {
984 // TODO check if this is ok for the user
985 LOG(DFATAL) << "XPRESS does not support contradictory bounds on range "
986 "constraints! ["
987 << lb << ", " << ub << "] will be converted to " << ub << ", "
988 << (ub - std::abs(ub - lb)) << "]";
989 }
990 rhs = ub;
991 range = std::abs(ub - lb); // This happens implicitly by XPRSaddrows()
992 sense = 'R';
993 } else if (ub < XPRS_PLUSINFINITY || (std::abs(ub) == XPRS_PLUSINFINITY &&
994 std::abs(lb) > XPRS_PLUSINFINITY)) {
995 // Finite upper, infinite lower bound -> this is a <= constraint
996 rhs = ub;
997 range = 0.0;
998 sense = 'L';
999 } else if (lb > XPRS_MINUSINFINITY || (std::abs(lb) == XPRS_PLUSINFINITY &&
1000 std::abs(ub) > XPRS_PLUSINFINITY)) {
1001 // Finite lower, infinite upper bound -> this is a >= constraint
1002 rhs = lb;
1003 range = 0.0;
1004 sense = 'G';
1005 } else {
1006 // Lower and upper bound are both infinite.
1007 // This is used for example in .mps files to specify alternate
1008 // objective functions.
1009 // A free row is denoted by sense 'N' and we can specify arbitrary
1010 // right-hand sides since they are ignored anyway. We just pick the
1011 // bound with smaller absolute value.
1012 DCHECK_GE(std::abs(lb), XPRS_PLUSINFINITY);
1013 DCHECK_GE(std::abs(ub), XPRS_PLUSINFINITY);
1014 if (std::abs(lb) < std::abs(ub))
1015 rhs = lb;
1016 else
1017 rhs = ub;
1018 range = 0.0;
1019 sense = 'N';
1020 }
1021}
1022
1023void XpressInterface::SetConstraintBounds(int index, double lb, double ub) {
1025
1026 // Changing rhs, sense, or range of a constraint is not too slow.
1027 // Still, doing all the updates in one large operation is faster.
1028 // Note however that if we do not want to re-extract the full model
1029 // for each solve (supportIncrementalExtraction is true) then we MUST
1030 // update the constraint here, otherwise we lose this update information.
1031
1032 if (!supportIncrementalExtraction &&
1033 !(slowUpdates & SlowSetConstraintBounds)) {
1034 InvalidateModelSynchronization();
1035 } else {
1036 if (constraint_is_extracted(index)) {
1037 // Constraint is already extracted, so we must update its bounds
1038 // and its type.
1039 DCHECK(mLp != nullptr);
1040 char sense;
1041 double range, rhs;
1042 MakeRhs(lb, ub, rhs, sense, range);
1043 if (sense == 'R') {
1044 // Rather than doing the complicated analysis required for
1045 // XPRSchgrhsrange(), we first convert the row into an 'L' row
1046 // with defined rhs and then change the range value.
1047 CHECK_STATUS(XPRSchgrowtype(mLp, 1, &index, "L"));
1048 CHECK_STATUS(XPRSchgrhs(mLp, 1, &index, &rhs));
1049 CHECK_STATUS(XPRSchgrhsrange(mLp, 1, &index, &range));
1050 } else {
1051 CHECK_STATUS(XPRSchgrowtype(mLp, 1, &index, &sense));
1052 CHECK_STATUS(XPRSchgrhs(mLp, 1, &index, &rhs));
1053 }
1054 } else {
1055 // Constraint is not yet extracted. It is sufficient to mark the
1056 // modeling object as "out of sync"
1057 InvalidateModelSynchronization();
1058 }
1059 }
1060}
1061
1063 // This is currently only invoked when a new constraint is created,
1064 // see MPSolver::MakeRowConstraint().
1065 // At this point we only have the lower and upper bounds of the
1066 // constraint. We could immediately call XPRSaddrows() here but it is
1067 // usually much faster to handle the fully populated constraint in
1068 // ExtractNewConstraints() right before the solve.
1069
1070 // TODO
1071 // Make new constraints basic (rowstat[jrow]=1)
1072 // Try not to delete basic variables, or non-basic constraints.
1073 InvalidateModelSynchronization();
1074}
1075
1077 // This is currently only invoked when a new variable is created,
1078 // see MPSolver::MakeVar().
1079 // At this point the variable does not appear in any constraints or
1080 // the objective function. We could invoke XPRSaddcols() to immediately
1081 // create the variable here, but it is usually much faster to handle the
1082 // fully set-up variable in ExtractNewVariables() right before the solve.
1083
1084 // TODO
1085 // Make new variables non-basic at their lower bound (colstat[icol]=0), unless
1086 // a variable has an infinite lower bound and a finite upper bound, in which
1087 // case make the variable non-basic at its upper bound (colstat[icol]=2) Try
1088 // not to delete basic variables, or non-basic constraints.
1089 InvalidateModelSynchronization();
1090}
1091
1093 MPVariable const* const variable,
1094 double new_value, double) {
1096
1097 fixedOrderCoefficientsPerConstraint[constraint->index()][variable->index()] =
1098 new_value;
1099
1100 // Changing a single coefficient in the matrix is potentially pretty
1101 // slow since that coefficient has to be found in the sparse matrix
1102 // representation. So by default we don't perform this update immediately
1103 // but instead mark the low-level modeling object "out of sync".
1104 // If we want to support incremental extraction then we MUST perform
1105 // the modification immediately, or we will lose it.
1106
1107 if (!supportIncrementalExtraction && !(slowUpdates & SlowSetCoefficient)) {
1108 InvalidateModelSynchronization();
1109 } else {
1110 int const row = constraint->index();
1111 int const col = variable->index();
1113 // If row and column are both extracted then we can directly
1114 // update the modeling object
1115 DCHECK_LE(row, last_constraint_index_);
1116 DCHECK_LE(col, last_variable_index_);
1117 CHECK_STATUS(XPRSchgcoef(mLp, row, col, new_value));
1118 } else {
1119 // If either row or column is not yet extracted then we can
1120 // defer the update to ExtractModel()
1121 InvalidateModelSynchronization();
1122 }
1123 }
1124}
1125
1127 int const row = constraint->index();
1128 if (!constraint_is_extracted(row))
1129 // There is nothing to do if the constraint was not even extracted.
1130 return;
1131
1132 fixedOrderCoefficientsPerConstraint.erase(constraint->index());
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();
1155 if (variable_is_extracted(col)) {
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();
1170 if (!variable_is_extracted(col))
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)) {
1184 CHECK_STATUS(XPRSchgobj(mLp, 1, &col, &coefficient));
1185 } else {
1186 InvalidateModelSynchronization();
1187 }
1188}
1189
1191 // Changing the objective offset is O(1), so we always do it immediately.
1193 CHECK_STATUS(setobjoffset(mLp, value));
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()) {
1301 return XpressToMPSolverBasisStatus(mRstat[constraint_index]);
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 std::vector<int> delayedRows;
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 = fixedOrderCoefficientsPerConstraint[ct->index()];
1569 for (auto [idx, coeff] : coeffs) {
1570 if (variable_is_extracted(idx)) {
1571 DCHECK_LT(nextNz, cols);
1572 DCHECK_LT(idx, cols);
1573 rmatind[nextNz] = idx;
1574 rmatval[nextNz] = coeff;
1575 ++nextNz;
1576 }
1577 }
1578 if (ct->is_lazy()) {
1579 delayedRows.push_back(offset + c);
1580 }
1581 }
1582 if (nextRow > 0) {
1583 CHECK_STATUS(XPRSaddrows(mLp, nextRow, nextNz, sense.get(), rhs.get(),
1584 rngval.get(), rmatbeg.get(), rmatind.get(),
1585 rmatval.get()));
1586 }
1587 if (!delayedRows.empty()) {
1589 mLp, static_cast<int>(delayedRows.size()), delayedRows.data()));
1590 }
1591 }
1592 } catch (...) {
1593 // Undo all changes in case of error.
1594 int const rows = getnumrows(mLp);
1595 std::vector<int> rows_to_delete;
1596 for (int i = offset; i < rows; ++i) rows_to_delete.push_back(i);
1597 if (rows > offset)
1598 (void)XPRSdelrows(mLp, rows_to_delete.size(), rows_to_delete.data());
1599 std::vector<MPConstraint*> const& constraints = solver_->constraints();
1600 int const size = constraints.size();
1601 for (int i = offset; i < size; ++i) set_constraint_as_extracted(i, false);
1602 throw;
1603 }
1604 }
1605}
1606
1607// Extract the objective function.
1609 // NOTE: The code assumes that the objective expression does not contain
1610 // any non-zero duplicates.
1611
1612 int const cols = getnumcols(mLp);
1613 // DCHECK_EQ(last_variable_index_, cols);
1614
1615 unique_ptr<int[]> ind(new int[cols]);
1616 unique_ptr<double[]> val(new double[cols]);
1617 for (int j = 0; j < cols; ++j) {
1618 ind[j] = j;
1619 val[j] = 0.0;
1620 }
1621
1622 const auto& coeffs = solver_->objective_->coefficients_;
1623 for (auto coeff : coeffs) {
1624 int const idx = coeff.first->index();
1625 if (variable_is_extracted(idx)) {
1626 DCHECK_LT(idx, cols);
1627 val[idx] = coeff.second;
1628 }
1629 }
1630
1631 CHECK_STATUS(XPRSchgobj(mLp, cols, ind.get(), val.get()));
1632 CHECK_STATUS(setobjoffset(mLp, solver_->Objective().offset()));
1633}
1634
1635// ------ Parameters -----
1636
1642
1644 if (mMip) {
1646 } else {
1647 LOG(WARNING) << "The relative MIP gap is only available "
1648 << "for discrete problems.";
1649 }
1650}
1651
1655
1659
1661 auto const presolve = static_cast<MPSolverParameters::PresolveValues>(value);
1662
1663 switch (presolve) {
1666 return;
1669 return;
1670 }
1672}
1673
1674// Sets the scaling mode.
1676 auto const scaling = static_cast<MPSolverParameters::ScalingValues>(value);
1677
1678 switch (scaling) {
1681 break;
1684 // In Xpress, scaling is not a binary on/off control, but a bit vector
1685 // control setting it to 1 would only enable bit 1. Instead, we reset it
1686 // to its default (163 for the current version 8.6) Alternatively, we
1687 // could call CHECK_STATUS(XPRSsetintcontrol(mLp, XPRS_SCALING, 163));
1688 break;
1689 }
1690}
1691
1692// Sets the LP algorithm : primal, dual or barrier. Note that XPRESS offers
1693// other LP algorithm (e.g. network) and automatic selection
1695 auto const algorithm =
1696 static_cast<MPSolverParameters::LpAlgorithmValues>(value);
1697
1698 int alg = 1;
1699
1700 switch (algorithm) {
1702 alg = 2;
1703 break;
1705 alg = 3;
1706 break;
1708 alg = 4;
1709 break;
1710 }
1711
1712 if (alg == XPRS_DEFAULTALG) {
1714 } else {
1716 }
1717}
1719 const std::vector<MPSolver::BasisStatus>& statuses) {
1720 std::vector<int> result;
1721 result.resize(statuses.size());
1722 std::transform(statuses.cbegin(), statuses.cend(), result.begin(),
1724 return result;
1725}
1726
1728 const std::vector<MPSolver::BasisStatus>& variable_statuses,
1729 const std::vector<MPSolver::BasisStatus>& constraint_statuses) {
1730 if (mMip) {
1731 LOG(DFATAL) << __FUNCTION__ << " is only available for LP problems";
1732 return;
1733 }
1734 initial_variables_basis_status_ = XpressBasisStatusesFrom(variable_statuses);
1735 initial_constraint_basis_status_ =
1736 XpressBasisStatusesFrom(constraint_statuses);
1737}
1738
1739bool XpressInterface::readParameters(std::istream& is, char sep) {
1740 // - parameters must be specified as NAME=VALUE
1741 // - settings must be separated by sep
1742 // - any whitespace is ignored
1743 // - string parameters are not supported
1744
1745 std::string name(""), value("");
1746 bool inValue = false;
1747
1748 while (is) {
1749 int c = is.get();
1750 if (is.eof()) break;
1751 if (c == '=') {
1752 if (inValue) {
1753 LOG(DFATAL) << "Failed to parse parameters in " << SolverVersion();
1754 return false;
1755 }
1756 inValue = true;
1757 } else if (c == sep) {
1758 // End of parameter setting
1759 if (name.size() == 0 && value.size() == 0) {
1760 // Ok to have empty "lines".
1761 continue;
1762 } else if (name.size() == 0) {
1763 LOG(DFATAL) << "Parameter setting without name in " << SolverVersion();
1764 } else if (!readParameter(mLp, name, value))
1765 return false;
1766
1767 // Reset for parsing the next parameter setting.
1768 name = "";
1769 value = "";
1770 inValue = false;
1771 } else if (std::isspace(c)) {
1772 continue;
1773 } else if (inValue) {
1774 value += (char)c;
1775 } else {
1776 name += (char)c;
1777 }
1778 }
1779 if (inValue) return readParameter(mLp, name, value);
1780
1781 return true;
1782}
1783
1784bool XpressInterface::ReadParameterFile(std::string const& filename) {
1785 // Return true on success and false on error.
1786 std::ifstream s(filename);
1787 if (!s) return false;
1788 return readParameters(s, '\n');
1789}
1790
1792 return ".prm";
1793}
1794
1796 int status;
1797
1798 // Delete cached information
1799 mCstat.clear();
1800 mRstat.clear();
1801
1802 WallTimer timer;
1803 timer.Start();
1804
1805 // Set incrementality
1806 auto const inc = static_cast<MPSolverParameters::IncrementalityValues>(
1808 switch (inc) {
1810 Reset(); // This should not be required but re-extracting everything
1811 // may be faster, so we do it.
1812 break;
1813 }
1816 break;
1817 }
1818 }
1819
1820 // Extract the model to be solved.
1821 // If we don't support incremental extraction and the low-level modeling
1822 // is out of sync then we have to re-extract everything. Note that this
1823 // will lose MIP starts or advanced basis information from a previous
1824 // solve.
1825 if (!supportIncrementalExtraction && sync_status_ == MUST_RELOAD) Reset();
1826 ExtractModel();
1827 VLOG(1) << absl::StrFormat("Model build in %.3f seconds.", timer.Get());
1828
1829 // Set log level.
1830 XPRSsetintcontrol(mLp, XPRS_OUTPUTLOG, quiet() ? 0 : 1);
1831 // Set parameters.
1832 // We first set our internal MPSolverParameters from 'param' and then set
1833 // any user-specified internal solver parameters via
1834 // solver_specific_parameter_string_.
1835 // Default MPSolverParameters can override custom parameters while specific
1836 // parameters allow a higher level of customization (for example for
1837 // presolving) and therefore we apply MPSolverParameters first.
1838 SetParameters(param);
1839 solver_->SetSolverSpecificParametersAsString(
1840 solver_->solver_specific_parameter_string_);
1841 if (solver_->time_limit()) {
1842 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
1843 // In Xpress, a time limit should usually have a negative sign. With a
1844 // positive sign, the solver will only stop when a solution has been found.
1846 -1 * solver_->time_limit_in_secs()));
1847 }
1848
1849 // Load basis if present
1850 // TODO : check number of variables / constraints
1851 if (!mMip && !initial_variables_basis_status_.empty() &&
1852 !initial_constraint_basis_status_.empty()) {
1853 CHECK_STATUS(XPRSloadbasis(mLp, initial_constraint_basis_status_.data(),
1854 initial_variables_basis_status_.data()));
1855 }
1856
1857 // Set the hint (if any)
1858 this->AddSolutionHintToOptimizer();
1859
1860 // Add opt node callback to optimizer. We have to do this here (just before
1861 // solve) to make sure the variables are fully initialized
1862 MPCallbackWrapper* mp_callback_wrapper = nullptr;
1863 if (callback_ != nullptr) {
1864 mp_callback_wrapper = new MPCallbackWrapper(callback_);
1866 static_cast<void*>(mp_callback_wrapper), 0));
1867 }
1868
1869 // Solve.
1870 // Do not CHECK_STATUS here since some errors (for example CPXERR_NO_MEMORY)
1871 // still allow us to query useful information.
1872 timer.Restart();
1873
1874 int xpress_stat = 0;
1875 if (mMip) {
1876 status = XPRSmipoptimize(mLp, "");
1877 XPRSgetintattrib(mLp, XPRS_MIPSTATUS, &xpress_stat);
1878 } else {
1879 status = XPRSlpoptimize(mLp, "");
1880 XPRSgetintattrib(mLp, XPRS_LPSTATUS, &xpress_stat);
1881 }
1882
1883 if (mp_callback_wrapper != nullptr) {
1884 mp_callback_wrapper->LogCaughtExceptions();
1885 delete mp_callback_wrapper;
1886 }
1887
1888 if (!(mMip ? (xpress_stat == XPRS_MIP_OPTIMAL)
1889 : (xpress_stat == XPRS_LP_OPTIMAL))) {
1890 XPRSpostsolve(mLp);
1891 }
1892
1893 // Disable screen output right after solve
1895
1896 if (status) {
1897 VLOG(1) << absl::StrFormat("Failed to optimize MIP. Error %d", status);
1898 // NOTE: We do not return immediately since there may be information
1899 // to grab (for example an incumbent)
1900 } else {
1901 VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get());
1902 }
1903
1904 VLOG(1) << absl::StrFormat("XPRESS solution status %d.", xpress_stat);
1905
1906 // Figure out what solution we have.
1907 bool const feasible = (mMip ? (xpress_stat == XPRS_MIP_OPTIMAL ||
1908 xpress_stat == XPRS_MIP_SOLUTION)
1909 : (!mMip && xpress_stat == XPRS_LP_OPTIMAL));
1910
1911 // Get problem dimensions for solution queries below.
1912 int const rows = getnumrows(mLp);
1913 int const cols = getnumcols(mLp);
1914 DCHECK_EQ(rows, solver_->constraints_.size());
1915 DCHECK_EQ(cols, solver_->variables_.size());
1916
1917 // Capture objective function value.
1920 if (feasible) {
1921 if (mMip) {
1925 } else {
1927 }
1928 }
1929 VLOG(1) << "objective=" << objective_value_
1930 << ", bound=" << best_objective_bound_;
1931
1932 // Capture primal and dual solutions
1933 if (mMip) {
1934 // If there is a primal feasible solution then capture it.
1935 if (feasible) {
1936 if (cols > 0) {
1937 unique_ptr<double[]> x(new double[cols]);
1938 CHECK_STATUS(XPRSgetmipsol(mLp, x.get(), 0));
1939 for (int i = 0; i < solver_->variables_.size(); ++i) {
1940 MPVariable* const var = solver_->variables_[i];
1941 var->set_solution_value(x[i]);
1942 VLOG(3) << var->name() << ": value =" << x[i];
1943 }
1944 }
1945 } else {
1946 for (auto& variable : solver_->variables_)
1947 variable->set_solution_value(XPRS_NAN);
1948 }
1949
1950 // MIP does not have duals
1951 for (auto& variable : solver_->variables_)
1952 variable->set_reduced_cost(XPRS_NAN);
1953 for (auto& constraint : solver_->constraints_)
1954 constraint->set_dual_value(XPRS_NAN);
1955 } else {
1956 // Continuous problem.
1957 if (cols > 0) {
1958 unique_ptr<double[]> x(new double[cols]);
1959 unique_ptr<double[]> dj(new double[cols]);
1960 if (feasible) CHECK_STATUS(XPRSgetlpsol(mLp, x.get(), 0, 0, dj.get()));
1961 for (int i = 0; i < solver_->variables_.size(); ++i) {
1962 MPVariable* const var = solver_->variables_[i];
1963 var->set_solution_value(x[i]);
1964 bool value = false, dual = false;
1965
1966 if (feasible) {
1967 var->set_solution_value(x[i]);
1968 value = true;
1969 } else {
1971 }
1972 if (feasible) {
1973 var->set_reduced_cost(dj[i]);
1974 dual = true;
1975 } else {
1977 }
1978 VLOG(3) << var->name() << ":"
1979 << (value ? absl::StrFormat(" value = %f", x[i]) : "")
1980 << (dual ? absl::StrFormat(" reduced cost = %f", dj[i]) : "");
1981 }
1982 }
1983
1984 if (rows > 0) {
1985 unique_ptr<double[]> pi(new double[rows]);
1986 if (feasible) {
1987 CHECK_STATUS(XPRSgetlpsol(mLp, 0, 0, pi.get(), 0));
1988 }
1989 for (int i = 0; i < solver_->constraints_.size(); ++i) {
1990 MPConstraint* const ct = solver_->constraints_[i];
1991 bool dual = false;
1992 if (feasible) {
1993 ct->set_dual_value(pi[i]);
1994 dual = true;
1995 } else {
1997 }
1998 VLOG(4) << "row " << ct->index() << ":"
1999 << (dual ? absl::StrFormat(" dual = %f", pi[i]) : "");
2000 }
2001 }
2002 }
2003
2004 // Map XPRESS status to more generic solution status in MPSolver
2005 if (mMip) {
2006 switch (xpress_stat) {
2007 case XPRS_MIP_OPTIMAL:
2009 break;
2010 case XPRS_MIP_INFEAS:
2012 break;
2013 case XPRS_MIP_UNBOUNDED:
2015 break;
2016 default:
2018 break;
2019 }
2020 } else {
2021 switch (xpress_stat) {
2022 case XPRS_LP_OPTIMAL:
2024 break;
2025 case XPRS_LP_INFEAS:
2027 break;
2028 case XPRS_LP_UNBOUNDED:
2030 break;
2031 default:
2033 break;
2034 }
2035 }
2036
2038 return result_status_;
2039}
2040
2041namespace {
2042template <class T>
2043struct getNameFlag;
2044
2045template <>
2046struct getNameFlag<MPVariable> {
2047 enum { value = XPRS_NAMES_COLUMN };
2048};
2049
2050template <>
2051struct getNameFlag<MPConstraint> {
2052 enum { value = XPRS_NAMES_ROW };
2053};
2054
2055template <class T>
2056// T = MPVariable | MPConstraint
2057// or any class that has a public method name() const
2058void ExtractNames(XPRSprob mLp, const std::vector<T*>& objects) {
2059 const bool have_names =
2060 std::any_of(objects.begin(), objects.end(),
2061 [](const T* x) { return !x->name().empty(); });
2062
2063 // FICO XPRESS requires a single large const char* such as
2064 // "name1\0name2\0name3"
2065 // See
2066 // https://www.fico.com/fico-xpress-optimization/docs/latest/solver/optimizer/HTML/XPRSaddnames.html
2067 if (have_names) {
2068 std::vector<char> all_names;
2069 for (const auto& x : objects) {
2070 const std::string& current_name = x->name();
2071 std::copy(current_name.begin(), current_name.end(),
2072 std::back_inserter(all_names));
2073 all_names.push_back('\0');
2074 }
2075
2076 // Remove trailing '\0', if any
2077 // Note : Calling pop_back on an empty container is undefined behavior.
2078 if (!all_names.empty() && all_names.back() == '\0') all_names.pop_back();
2079
2080 CHECK_STATUS(XPRSaddnames(mLp, getNameFlag<T>::value, all_names.data(), 0,
2081 objects.size() - 1));
2082 }
2083}
2084} // namespace
2085
2086void XpressInterface::Write(const std::string& filename) {
2087 if (sync_status_ == MUST_RELOAD) {
2088 Reset();
2089 }
2090 ExtractModel();
2091
2092 ExtractNames(mLp, solver_->variables_);
2093 ExtractNames(mLp, solver_->constraints_);
2094
2095 VLOG(1) << "Writing Xpress MPS \"" << filename << "\".";
2096 const int status = XPRSwriteprob(mLp, filename.c_str(), "");
2097 if (status) {
2098 LOG(ERROR) << "Xpress: Failed to write MPS!";
2099 }
2100}
2101// TODO useless ?
2102template <class Container>
2103void splitMyString(const std::string& str, Container& cont, char delim = ' ') {
2104 std::stringstream ss(str);
2105 std::string token;
2106 while (std::getline(ss, token, delim)) {
2107 cont.push_back(token);
2108 }
2109}
2110
2111bool stringToCharPtr(const std::string& var, const char** out) {
2112 *out = var.c_str();
2113 return true;
2114}
2115
2116#define setParamIfPossible_MACRO(target_map, setter, converter, type) \
2117 { \
2118 auto matchingParamIter = (target_map).find(paramAndValuePair.first); \
2119 if (matchingParamIter != (target_map).end()) { \
2120 type convertedValue; \
2121 bool ret = converter(paramAndValuePair.second, &convertedValue); \
2122 if (ret) { \
2123 VLOG(1) << "Setting parameter " << paramAndValuePair.first \
2124 << " to value " << convertedValue << std::endl; \
2125 } \
2126 setter(mLp, matchingParamIter->second, convertedValue); \
2127 continue; \
2128 } \
2129 }
2130
2131bool XpressInterface::SetSolverSpecificParametersAsString(
2132 const std::string& parameters) {
2133 if (parameters.empty()) return true;
2134
2135 std::vector<std::pair<std::string, std::string> > paramAndValuePairList;
2136
2137 std::stringstream ss(parameters);
2138 std::string paramName;
2139 while (std::getline(ss, paramName, ' ')) {
2140 std::string paramValue;
2141 if (std::getline(ss, paramValue, ' ')) {
2142 paramAndValuePairList.push_back(std::make_pair(paramName, paramValue));
2143 } else {
2144 LOG(ERROR) << "No value for parameter " << paramName << " : function "
2145 << __FUNCTION__ << std::endl;
2146 return false;
2147 }
2148 }
2149
2150 for (auto& paramAndValuePair : paramAndValuePairList) {
2151 setParamIfPossible_MACRO(mapIntegerControls_, XPRSsetintcontrol,
2152 absl::SimpleAtoi<int>, int);
2153 setParamIfPossible_MACRO(mapDoubleControls_, XPRSsetdblcontrol,
2154 absl::SimpleAtod, double);
2155 setParamIfPossible_MACRO(mapStringControls_, XPRSsetstrcontrol,
2156 stringToCharPtr, const char*);
2157 setParamIfPossible_MACRO(mapInteger64Controls_, XPRSsetintcontrol64,
2158 absl::SimpleAtoi<int64_t>, int64_t);
2159 LOG(ERROR) << "Unknown parameter " << paramName << " : function "
2160 << __FUNCTION__ << std::endl;
2161 return false;
2162 }
2163 return true;
2164}
2165
2166/*****************************************************************************\
2167* Name: optimizermsg
2168* Purpose: Display Optimizer error messages and warnings.
2169* Arguments: const char *sMsg Message string
2170* int nLen Message length
2171* int nMsgLvl Message type
2172* Return Value: None
2173\*****************************************************************************/
2174void XPRS_CC optimizermsg(XPRSprob prob, void* data, const char* sMsg, int nLen,
2175 int nMsgLvl) {
2176 auto* xprs = reinterpret_cast<operations_research::XpressInterface*>(data);
2177 if (!xprs->quiet()) {
2178 switch (nMsgLvl) {
2179 /* Print Optimizer error messages and warnings */
2180 case 4: /* error */
2181 case 3: /* warning */
2182 /* Ignore other messages */
2183 case 2: /* dialogue */
2184 case 1: /* information */
2185 printf("%*s\n", nLen, sMsg);
2186 break;
2187 /* Exit and flush buffers */
2188 default:
2189 fflush(nullptr);
2190 break;
2191 }
2192 }
2193}
2194
2195void XpressInterface::AddSolutionHintToOptimizer() {
2196 // Currently the XPRESS API does not handle clearing out previous hints
2197 const std::size_t len = solver_->solution_hint_.size();
2198 if (len == 0) {
2199 // hint is empty, nothing to do
2200 return;
2201 }
2202 unique_ptr<int[]> col_ind(new int[len]);
2203 unique_ptr<double[]> val(new double[len]);
2204
2205 for (std::size_t i = 0; i < len; ++i) {
2206 col_ind[i] = solver_->solution_hint_[i].first->index();
2207 val[i] = solver_->solution_hint_[i].second;
2208 }
2209 addhint(mLp, len, val.get(), col_ind.get());
2210}
2211
2213 if (callback_ != nullptr) {
2214 // replace existing callback by removing it first
2216 }
2217 callback_ = mp_callback;
2218}
2219
2220// This is the call-back called by XPRESS when it finds a new MIP solution
2221// NOTE(user): This function must have this exact API, because we are passing
2222// it to XPRESS as a callback.
2223void XPRS_CC XpressIntSolCallbackImpl(XPRSprob cbprob, void* cbdata) {
2224 auto callback_with_context = static_cast<MPCallbackWrapper*>(cbdata);
2225 if (callback_with_context == nullptr ||
2226 callback_with_context->GetCallback() == nullptr) {
2227 // nothing to do
2228 return;
2229 }
2230 try {
2231 std::unique_ptr<XpressMPCallbackContext> cb_context =
2232 std::make_unique<XpressMPCallbackContext>(
2233 &cbprob, MPCallbackEvent::kMipSolution, getnodecnt(cbprob));
2234 callback_with_context->GetCallback()->RunCallback(cb_context.get());
2235 } catch (std::exception&) {
2236 callback_with_context->CatchException(cbprob);
2237 }
2238}
2239
2243
2245 if (variable_values_.empty()) {
2246 int num_vars = getnumcols(*xprsprob_);
2247 variable_values_.resize(num_vars);
2248 CHECK_STATUS(XPRSgetmipsol(*xprsprob_, variable_values_.data(), 0));
2249 }
2250 return variable_values_[variable->index()];
2251}
2252
2254 const absl::flat_hash_map<const MPVariable*, double>& solution) {
2255 // Currently the XPRESS API does not handle clearing out previous hints
2256 const std::size_t len = solution.size();
2257 if (len == 0) {
2258 // hint is empty, do nothing
2259 return NAN;
2260 }
2262 // Currently, XPRESS does not handle adding a new MIP solution inside the
2263 // "cbintsol" callback (cb for new MIP solutions that is used here)
2264 // So we have to prevent the user from adding a solution
2265 // TODO: remove this workaround when it is handled in XPRESS
2266 LOG(WARNING)
2267 << "XPRESS does not currently allow suggesting MIP solutions after "
2268 "a kMipSolution event. Try another call-back.";
2269 return NAN;
2270 }
2271 unique_ptr<int[]> colind(new int[len]);
2272 unique_ptr<double[]> val(new double[len]);
2273 int i = 0;
2274 for (const auto& [var, value] : solution) {
2275 colind[i] = var->index();
2276 val[i] = value;
2277 ++i;
2278 }
2279 addhint(*xprsprob_, len, val.get(), colind.get());
2280
2281 // XPRESS doesn't guarantee if nor when it will test the suggested solution.
2282 // So we return NaN because we can't know the actual objective value.
2283 return NAN;
2284}
2285
2286namespace {
2287
2288// See MpSolverInterfaceFactoryRepository for details.
2289const void* const kRegisterXpress ABSL_ATTRIBUTE_UNUSED = [] {
2291 [](MPSolver* const solver) { return new XpressInterface(solver, false); },
2293 []() { return XpressIsCorrectlyInstalled(); });
2294 return nullptr;
2295}();
2296
2297// See MpSolverInterfaceFactoryRepository for details.
2298const void* const kRegisterXpressMip ABSL_ATTRIBUTE_UNUSED = [] {
2300 [](MPSolver* const solver) { return new XpressInterface(solver, true); },
2302 []() { return XpressIsCorrectlyInstalled(); });
2303 return nullptr;
2304}();
2305
2306} // namespace
2307
2308} // namespace operations_research
double Get() const
Definition timer.h:44
void Restart()
Definition timer.h:34
void Start()
Definition timer.h:30
bool is_lazy() const
Advanced usage: returns true if the constraint is "lazy" (see below).
void set_dual_value(double dual_value)
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
static MPSolverInterfaceFactoryRepository * GetInstance()
void Register(MPSolverInterfaceFactory factory, MPSolver::OptimizationProblemType problem_type, std::function< bool()> is_runtime_ready={})
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)
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
bool variable_is_extracted(int var_index) const
bool constraint_is_extracted(int ct_index) const
static constexpr int64_t kUnknownNumberOfNodes
void SetMIPParameters(const MPSolverParameters &param)
void SetCommonParameters(const MPSolverParameters &param)
PresolveValues
For each categorical parameter, enumeration of possible values.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
@ PRESOLVE
Advanced usage: presolve mode.
@ LP_ALGORITHM
Algorithm to solve linear programs.
@ SCALING
Advanced usage: enable or disable matrix scaling.
IncrementalityValues
Advanced usage: Incrementality options.
@ INCREMENTALITY_OFF
Start solve from scratch.
ScalingValues
Advanced usage: Scaling options.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
@ FEASIBLE
feasible, or stopped by limit.
@ INFEASIBLE
proven infeasible.
@ ABNORMAL
abnormal, i.e., error of some kind.
The class for variables of a Mathematical Programming (MP) model.
bool integer() const
Returns the integrality requirement of the variable.
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
const std::string & name() const
Returns the name of the variable.
void set_reduced_cost(double reduced_cost)
void set_solution_value(double value)
int index() const
Returns the index of the variable in the MPSolver::variables_.
MPSolver::BasisStatus row_status(int constraint_index) const override
MPSolver::BasisStatus column_status(int variable_index) const override
void SetOptimizationDirection(bool maximize) override
void AddRowConstraint(MPConstraint *ct) override
void SetObjectiveOffset(double value) override
double ComputeExactConditionNumber() const override
virtual bool ReadParameterFile(std::string const &filename)
void SetCoefficient(MPConstraint *constraint, MPVariable const *variable, double new_value, double old_value) override
void SetVariableBounds(int var_index, double lb, double ub) override
XpressInterface(MPSolver *solver, bool mip)
void Write(const std::string &filename) override
MPSolver::ResultStatus Solve(MPSolverParameters const &param) override
void ClearConstraint(MPConstraint *constraint) override
void SetConstraintBounds(int row_index, double lb, double ub) override
void SetRelativeMipGap(double value) override
void SetDualTolerance(double value) override
void AddVariable(MPVariable *var) override
void SetObjectiveCoefficient(MPVariable const *variable, double coefficient) override
void SetStartingLpBasis(const std::vector< MPSolver::BasisStatus > &variable_statuses, const std::vector< MPSolver::BasisStatus > &constraint_statuses) override
void SetCallback(MPCallback *mp_callback) override
virtual std::string ValidFileExtensionForParameterFile() const
void SetPrimalTolerance(double value) override
void SetParameters(MPSolverParameters const &param) override
void SetVariableInteger(int var_index, bool integer) override
std::string SolverVersion() const override
void AddCut(const LinearRange &cutting_plane) override
XpressMPCallbackContext(XPRSprob *xprsprob, MPCallbackEvent event, int num_nodes)
double SuggestSolution(const absl::flat_hash_map< const MPVariable *, double > &solution) override
void AddLazyConstraint(const LinearRange &lazy_constraint) override
double VariableValue(const MPVariable *variable) override
#define CHECK_STATUS(s)
OR-Tools root namespace.
std::function< int(XPRSprob prob, int objidx)> XPRSdelobj
std::function< int(XPRSprob prob, void(XPRS_CC *f_intsol)(XPRSprob cbprob, void *cbdata), void *p, int priority)> XPRSaddcbintsol
std::function< int(XPRSprob prob, int control)> XPRSsetdefaultcontrol
std::function< int(XPRSprob prob, int nrows, const int rowind[], const char rowtype[])> XPRSchgrowtype
void addhint(const XPRSprob &mLp, int length, const double solval[], const int colind[])
std::function< int(XPRSprob prob, int nrows, const int rowind[])> XPRSloaddelayedrows
std::function< int(XPRSprob prob, int attrib, int *p_value)> XPRSgetintattrib
static std::map< std::string, int > & getMapIntControls()
std::function< int(XPRSprob prob, int attrib, double *p_value)> XPRSgetdblattrib
std::function< int(XPRSprob prob, const char *filename, const char *flags)> XPRSwriteprob
std::function< int(XPRSprob prob, void(XPRS_CC *f_message)(XPRSprob cbprob, void *cbdata, const char *msg, int msglen, int msgtype), void *p, int priority)> XPRSaddcbmessage
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
static MPSolver::BasisStatus XpressToMPSolverBasisStatus(int xpress_basis_status)
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)
bool stringToCharPtr(const std::string &var, const char **out)
std::function< int(XPRSprob prob, const int rowstat[], const int colstat[])> XPRSloadbasis
std::function< int(XPRSprob prob, int row, int col, double coef)> XPRSchgcoef
std::function< int(XPRSprob prob, int nrows, const int rowind[])> XPRSdelrows
std::function< int(XPRSprob prob, int ncols, const int colind[])> XPRSdelcols
std::string getSolverVersion(XPRSprob const &prob)
void interruptXPRESS(XPRSprob &xprsProb, CUSTOM_INTERRUPT_REASON reason)
std::function< int(XPRSprob prob, int control, int *p_value)> XPRSgetintcontrol
std::function< int(XPRSprob prob, void(XPRS_CC *f_intsol)(XPRSprob cbprob, void *cbdata), void *p)> XPRSremovecbintsol
std::function< int(XPRSprob prob, int ncols, const int colind[], const char coltype[])> XPRSchgcoltype
std::function< int(XPRSprob prob, const char *name, int *p_id, int *p_type)> XPRSgetcontrolinfo
std::function< int(XPRSprob prob, double x[], double slack[], double duals[], double djs[])> XPRSgetlpsol
std::function< int(XPRSprob prob)> XPRSpostsolve
std::function< int(XPRSprob prob, int nrows, const int rowind[], const double rng[])> XPRSchgrhsrange
int setobjoffset(const XPRSprob &mLp, double value)
std::function< int(XPRSprob prob, int type, const char names[], int first, int last)> XPRSaddnames
static int MPSolverToXpressBasisStatus(MPSolver::BasisStatus mpsolver_basis_status)
std::function< int(XPRSprob prob, int ncoefs, const int rowind[], const int colind[], const double rowcoef[])> XPRSchgmcoef
std::function< int(void)> XPRSfree
std::function< int(XPRSprob prob, int reason)> XPRSinterrupt
int getitcnt(const XPRSprob &mLp)
std::vector< int > XpressBasisStatusesFrom(const std::vector< MPSolver::BasisStatus > &statuses)
void splitMyString(const std::string &str, Container &cont, char delim=' ')
std::function< int(XPRSprob prob, int control, int value)> XPRSsetintcontrol
std::function< int(XPRSprob prob, int nrows, int ncoefs, const char rowtype[], const double rhs[], const double rng[], const int start[], const int colind[], const double rowcoef[])> XPRSaddrows
static std::map< std::string, int > & getMapStringControls()
std::function< int(XPRSprob prob, char *errmsg)> XPRSgetlasterror
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()
ClosedInterval::Iterator begin(ClosedInterval interval)
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
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)
#define XPRS_COMPUTEEXECSERVICE
#define XPRS_INPUTTOL
#define XPRS_BARORDERTHREADS
#define XPRS_EXTRAMIPENTS
#define XPRS_PREPERMUTE
#define XPRS_MPSOBJNAME
#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
#define XPRS_CLAMPING
#define XPRS_TYPE_INT
#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
#define XPRS_CHECKINPUTDATA
#define XPRS_PLUSINFINITY
#define XPRS_MIP_OPTIMAL
#define XPRS_SCALING
#define XPRS_TRACE
#define XPRS_PRIMALOPS
#define XPRS_NETCUTS
#define XPRS_EIGENVALUETOL
#define XPRS_DUMMYCONTROL
#define XPRS_BARSOLUTION
#define XPRS_CPUPLATFORM
#define XPRS_MIPTOL
#define XPRS_COMPUTEJOBPRIORITY
#define XPRS_CRASH
#define XPRS_NUMERICALEMPHASIS
#define XPRS_QCROOTALG
#define XPRS_MAXLOCALBACKTRACK
#define XPRS_RANDOMSEED
#define XPRS_CROSSOVERRELPIVOTTOLSAFE
#define XPRS_BASIC
#define XPRS_IFCHECKCONVEXITY
#define XPRS_MIPRESTARTFACTOR
#define XPRS_MIPSTATUS
#define XPRS_MIPABSSTOP
#define XPRS_MIPADDCUTOFF
#define XPRS_SERIALIZEPREINTSOL
#define XPRS_BARGAPSTOP
#define XPRS_HEURFORCESPECIALOBJ
#define XPRS_ALGAFTERCROSSOVER
#define XPRS_HEURDIVESPEEDUP
#define XPRS_MAXMIPTASKS
#define XPRS_BACKTRACKTIE
#define XPRS_MIP_UNBOUNDED
#define XPRS_QSIMPLEXOPS
#define XPRS_COLS
#define XPRS_PRECONVERTSEPARABLE
#define XPRS_PRECOMPONENTSEFFORT
#define XPRS_COMPUTEMATX_IISMAXTIME
#define XPRS_MPSBOUNDNAME
#define XPRS_DUALSTRATEGY
#define XPRS_BAROBJPERTURB
#define XPRS_MIPFRACREDUCE
#define XPRS_TREEGOMCUTS
#define XPRS_OBJSCALEFACTOR
#define XPRS_FREE_SUPER
#define XPRS_TYPE_INT64
#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
#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
#define XPRS_HEURSEARCHROOTSELECT
#define XPRS_MIPDUALREDUCTIONS
#define XPRS_PRECOEFELIM
#define XPRS_PREELIMQUAD
#define XPRS_FEASTOL
#define XPRS_MAXMIPSOL
#define XPRS_DUALPERTURB
#define XPRS_BARNUMSTABILITY
#define XPRS_PRESOLVEPASSES
#define XPRS_DETLOGFREQ
#define XPRS_CHOLESKYTOL
#define XPRS_BRANCHDISJ
#define XPRS_MAXTIME
#define XPRS_HEURFREQ
#define XPRS_IGNORECONTAINERMEMORYLIMIT
#define XPRS_PIVOTTOL
#define XPRS_PRIMALUNSHIFT
#define XPRS_MPSFORMAT
#define XPRS_CONCURRENTTHREADS
#define XPRS_HEURSEARCHEFFORT
#define XPRS_MIQCPALG
#define XPRS_MAXIMPLIEDBOUND
#define XPRS_PREPROBING
#define XPRS_OUTPUTTOL
#define XPRS_SBITERLIMIT
#define XPRS_PRESOLVE
#define XPRS_TREEQCCUTS
#define XPRS_ROOTPRESOLVE
#define XPRS_MPSECHO
#define XPRS_CROSSOVERTHREADS
#define XPRS_GOMCUTS
#define XPRS_BESTBOUND
#define XPRS_AUTOPERTURB
#define XPRS_PREBNDREDCONE
#define XPRS_MAXCHECKSONMAXCUTTIME
#define XPRS_VERSION
#define XPRS_MUTEXCALLBACKS
#define XPRS_CACHESIZE
#define XPRS_CHOLESKYALG
#define XPRS_BARSTART
#define XPRS_INDLINBIGM
#define XPRS_PRESORT
#define XPRS_LPLOG
#define XPRS_SYMSELECT
#define XPRS_AT_LOWER
#define XPRS_ELIMFILLIN
#define XPRS_SBESTIMATE
#define XPRS_HEURSELECT
#define XPRS_MAXIIS
#define XPRS_CORESPERCPU
#define XPRS_SIFTSWITCH
#define XPRS_MIPRELCUTOFF
#define XPRS_MPS18COMPATIBLE
#define XPRS_BARREFITER
#define XPRS_CUTSELECT
#define XPRS_BARSTARTWEIGHT
#define XPRS_OPTIMALITYTOLTARGET
#define XPRS_PPFACTOR
#define XPRS_TYPE_DOUBLE
#define XPRS_PREANALYTICCENTER
#define XPRS_IOTIMEOUT
#define XPRS_HEURDIVESTRATEGY
#define XPRS_BREADTHFIRST
#define XPRS_CPUTIME
#define XPRS_MIPRAMPUP
#define XPRS_SBBEST
#define XPRS_LOCALBACKTRACK
#define XPRS_RELPIVOTTOL
#define XPRS_MIPPRESOLVE
#define XPRS_TUNERMAXTIME
#define XPRS_FEASIBILITYPUMP
#define XPRS_TUNERMODE
#define XPRS_MIPOBJVAL
#define XPRS_PREDOMROW
#define XPRS_REPAIRINDEFINITEQMAX
#define XPRS_TUNERHISTORY
#define XPRS_REPAIRINDEFINITEQ
#define XPRS_BARKERNEL
#define XPRS_TREEDIAGNOSTICS
#define XPRS_PRECONFIGURATION
#define XPRS_CROSSOVEROPS
#define XPRS_ETATOL
#define XPRS_MIPABSGAPNOTIFYBOUND
#define XPRS_COMPUTEMATX_IIS
#define XPRS_OUTPUTLOG
#define XPRS_ELIMTOL
#define XPRS_NODES
#define XPRS_LP_OPTIMAL
#define XPRS_CONFLICTCUTS
#define XPRS_INDPRELINBIGM
#define XPRS_LPFLAGS
#define XPRS_PREDUPROW
#define XPRS_COVERCUTS
#define XPRS_QCCUTS
#define XPRS_PRIMALPERTURB
#define XPRS_ALGAFTERNETWORK
#define XPRS_DUALGRADIENT
#define XPRS_PENALTY
#define XPRS_AUTOCUTTING
#define XPRS_COMPUTELOG
#define XPRS_PRECOMPONENTS
#define XPRS_NODEPROBINGEFFORT
#define XPRS_PWLNONCONVEXTRANSFORMATION
#define XPRS_TREECOMPRESSION
#define XPRS_THREADS
#define XPRS_SYMMETRY
#define XPRS_FORCEOUTPUT
#define XPRS_TUNERMETHOD
#define XPRS_BARSTEPSTOP
#define XPRS_CUTSTRATEGY
#define XPRS_REFACTOR
#define XPRS_AUTOSCALING
#define XPRS_BARALG
#define XPRS_PRESOLVEMAXGROW
#define XPRS_TREEMEMORYLIMIT
#define XPRS_CUTFREQ
#define XPRS_MIPCOMPONENTS
#define XPRS_LPITERLIMIT
#define XPRS_TUNERSESSIONNAME
#define XPRS_BARORDER
#define XPRS_BARFREESCALE
#define XPRS_TUNEROUTPUTPATH
#define XPRS_MARKOWITZTOL
#define XPRS_HEURDIVERANDOMIZE
#define XPRS_PWLDUALREDUCTIONS
#define XPRS_AT_UPPER
#define XPRS_OUTPUTMASK
#define XPRS_DUALIZEOPS
#define XPRS_BARLARGEBOUND
#define XPRS_OPTIMALITYTOL
#define XPRS_KEEPNROWS
#define XPRS_MAXSCALEFACTOR
#define XPRS_CROSSOVERACCURACYTOL
#define XPRS_PRECONEDECOMP
#define XPRS_DUALTHREADS
#define XPRS_HEUREMPHASIS
#define XPRS_BARITERLIMIT
#define XPRS_KEEPBASIS
#define XPRS_BIGMMETHOD
#define XPRS_TUNERVERBOSE
#define XPRS_TUNERPERMUTE
#define XPRS_PREOBJCUTDETECT
#define XPRS_LOCALCHOICE
#define XPRS_LNPBEST
#define XPRS_PREPROTECTDUAL
#define XPRS_REPAIRINFEASTIMELIMIT
#define XPRS_MPSNAMELENGTH
#define XPRS_LPFOLDING
#define XPRS_GENCONSDUALREDUCTIONS
#define XPRS_LP_UNBOUNDED
#define XPRS_MATRIXTOL
#define XPRS_GENCONSABSTRANSFORMATION
#define XPRS_LPREFINEITERLIMIT
#define XPRS_PREDOMCOL
#define XPRS_HEURDIVESOFTROUNDING
#define XPRS_TUNEROUTPUT
#define XPRS_MIPREFINEITERLIMIT
#define XPRS_BARPERTURB
#define XPRS_REPAIRINFEASMAXTIME
#define XPRS_MAXTREEFILESIZE
#define XPRS_MIPRESTART
#define XPRS_HEURBEFORELP
#define XPRS_STOP_USER
#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
#define XPRS_FORCEPARALLELDUAL
#define XPRS_MIP_INFEAS
#define XPRS_MIPCONCURRENTSOLVES
#define XPRS_MPSRHSNAME
#define XPRS_CROSSOVERDRP
#define XPRS_MULTIOBJOPS
#define XPRS_MIPRESTARTGAPTHRESHOLD
#define XPRS_MINUSINFINITY
#define XPRS_MULTIOBJLOG
#define XPRS_PSEUDOCOST
#define XPRS_MAXCUTTIME
#define XPRS_PREIMPLICATIONS
#define XPRS_PREFOLDING
#define XPRS_MPSRANGENAME
#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
#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 setParamIfPossible_MACRO(target_map, setter, converter, type)
#define XPRS_NAN
#define XPRS_CONTINUOUS
#define XPRS_INTEGER