Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
xpress_interface.cc
Go to the documentation of this file.
1// Copyright 2019-2023 RTE
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// Initial version of this code was provided by RTE
15
16#include <algorithm>
17#include <clocale>
18#include <fstream>
19#include <istream>
20#include <limits>
21#include <memory>
22#include <mutex>
23#include <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)
236 << "AddLazyConstraint is not implemented yet 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
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 }
1579 if (nextRow > 0) {
1580 CHECK_STATUS(XPRSaddrows(mLp, nextRow, nextNz, sense.get(), rhs.get(),
1581 rngval.get(), rmatbeg.get(), rmatind.get(),
1582 rmatval.get()));
1583 }
1584 }
1585 } catch (...) {
1586 // Undo all changes in case of error.
1587 int const rows = getnumrows(mLp);
1588 std::vector<int> rows_to_delete;
1589 for (int i = offset; i < rows; ++i) rows_to_delete.push_back(i);
1590 if (rows > offset)
1591 (void)XPRSdelrows(mLp, rows_to_delete.size(), rows_to_delete.data());
1592 std::vector<MPConstraint*> const& constraints = solver_->constraints();
1593 int const size = constraints.size();
1594 for (int i = offset; i < size; ++i) set_constraint_as_extracted(i, false);
1595 throw;
1596 }
1597 }
1598}
1599
1600// Extract the objective function.
1602 // NOTE: The code assumes that the objective expression does not contain
1603 // any non-zero duplicates.
1604
1605 int const cols = getnumcols(mLp);
1606 // DCHECK_EQ(last_variable_index_, cols);
1607
1608 unique_ptr<int[]> ind(new int[cols]);
1609 unique_ptr<double[]> val(new double[cols]);
1610 for (int j = 0; j < cols; ++j) {
1611 ind[j] = j;
1612 val[j] = 0.0;
1613 }
1614
1615 const auto& coeffs = solver_->objective_->coefficients_;
1616 for (auto coeff : coeffs) {
1617 int const idx = coeff.first->index();
1618 if (variable_is_extracted(idx)) {
1619 DCHECK_LT(idx, cols);
1620 val[idx] = coeff.second;
1621 }
1622 }
1623
1624 CHECK_STATUS(XPRSchgobj(mLp, cols, ind.get(), val.get()));
1625 CHECK_STATUS(setobjoffset(mLp, solver_->Objective().offset()));
1626}
1627
1628// ------ Parameters -----
1629
1635
1637 if (mMip) {
1639 } else {
1640 LOG(WARNING) << "The relative MIP gap is only available "
1641 << "for discrete problems.";
1642 }
1643}
1644
1648
1652
1654 auto const presolve = static_cast<MPSolverParameters::PresolveValues>(value);
1655
1656 switch (presolve) {
1659 return;
1662 return;
1663 }
1665}
1666
1667// Sets the scaling mode.
1669 auto const scaling = static_cast<MPSolverParameters::ScalingValues>(value);
1670
1671 switch (scaling) {
1674 break;
1677 // In Xpress, scaling is not a binary on/off control, but a bit vector
1678 // control setting it to 1 would only enable bit 1. Instead, we reset it
1679 // to its default (163 for the current version 8.6) Alternatively, we
1680 // could call CHECK_STATUS(XPRSsetintcontrol(mLp, XPRS_SCALING, 163));
1681 break;
1682 }
1683}
1684
1685// Sets the LP algorithm : primal, dual or barrier. Note that XPRESS offers
1686// other LP algorithm (e.g. network) and automatic selection
1688 auto const algorithm =
1689 static_cast<MPSolverParameters::LpAlgorithmValues>(value);
1690
1691 int alg = 1;
1692
1693 switch (algorithm) {
1695 alg = 2;
1696 break;
1698 alg = 3;
1699 break;
1701 alg = 4;
1702 break;
1703 }
1704
1705 if (alg == XPRS_DEFAULTALG) {
1707 } else {
1709 }
1710}
1712 const std::vector<MPSolver::BasisStatus>& statuses) {
1713 std::vector<int> result;
1714 result.resize(statuses.size());
1715 std::transform(statuses.cbegin(), statuses.cend(), result.begin(),
1717 return result;
1718}
1719
1721 const std::vector<MPSolver::BasisStatus>& variable_statuses,
1722 const std::vector<MPSolver::BasisStatus>& constraint_statuses) {
1723 if (mMip) {
1724 LOG(DFATAL) << __FUNCTION__ << " is only available for LP problems";
1725 return;
1726 }
1727 initial_variables_basis_status_ = XpressBasisStatusesFrom(variable_statuses);
1728 initial_constraint_basis_status_ =
1729 XpressBasisStatusesFrom(constraint_statuses);
1730}
1731
1732bool XpressInterface::readParameters(std::istream& is, char sep) {
1733 // - parameters must be specified as NAME=VALUE
1734 // - settings must be separated by sep
1735 // - any whitespace is ignored
1736 // - string parameters are not supported
1737
1738 std::string name(""), value("");
1739 bool inValue = false;
1740
1741 while (is) {
1742 int c = is.get();
1743 if (is.eof()) break;
1744 if (c == '=') {
1745 if (inValue) {
1746 LOG(DFATAL) << "Failed to parse parameters in " << SolverVersion();
1747 return false;
1748 }
1749 inValue = true;
1750 } else if (c == sep) {
1751 // End of parameter setting
1752 if (name.size() == 0 && value.size() == 0) {
1753 // Ok to have empty "lines".
1754 continue;
1755 } else if (name.size() == 0) {
1756 LOG(DFATAL) << "Parameter setting without name in " << SolverVersion();
1757 } else if (!readParameter(mLp, name, value))
1758 return false;
1759
1760 // Reset for parsing the next parameter setting.
1761 name = "";
1762 value = "";
1763 inValue = false;
1764 } else if (std::isspace(c)) {
1765 continue;
1766 } else if (inValue) {
1767 value += (char)c;
1768 } else {
1769 name += (char)c;
1770 }
1771 }
1772 if (inValue) return readParameter(mLp, name, value);
1773
1774 return true;
1775}
1776
1777bool XpressInterface::ReadParameterFile(std::string const& filename) {
1778 // Return true on success and false on error.
1779 std::ifstream s(filename);
1780 if (!s) return false;
1781 return readParameters(s, '\n');
1782}
1783
1785 return ".prm";
1786}
1787
1789 int status;
1790
1791 // Delete cached information
1792 mCstat.clear();
1793 mRstat.clear();
1794
1795 WallTimer timer;
1796 timer.Start();
1797
1798 // Set incrementality
1799 auto const inc = static_cast<MPSolverParameters::IncrementalityValues>(
1801 switch (inc) {
1803 Reset(); // This should not be required but re-extracting everything
1804 // may be faster, so we do it.
1805 break;
1806 }
1809 break;
1810 }
1811 }
1812
1813 // Extract the model to be solved.
1814 // If we don't support incremental extraction and the low-level modeling
1815 // is out of sync then we have to re-extract everything. Note that this
1816 // will lose MIP starts or advanced basis information from a previous
1817 // solve.
1818 if (!supportIncrementalExtraction && sync_status_ == MUST_RELOAD) Reset();
1819 ExtractModel();
1820 VLOG(1) << absl::StrFormat("Model build in %.3f seconds.", timer.Get());
1821
1822 // Set log level.
1823 XPRSsetintcontrol(mLp, XPRS_OUTPUTLOG, quiet() ? 0 : 1);
1824 // Set parameters.
1825 // We first set our internal MPSolverParameters from 'param' and then set
1826 // any user-specified internal solver parameters via
1827 // solver_specific_parameter_string_.
1828 // Default MPSolverParameters can override custom parameters while specific
1829 // parameters allow a higher level of customization (for example for
1830 // presolving) and therefore we apply MPSolverParameters first.
1831 SetParameters(param);
1832 solver_->SetSolverSpecificParametersAsString(
1833 solver_->solver_specific_parameter_string_);
1834 if (solver_->time_limit()) {
1835 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
1836 // In Xpress, a time limit should usually have a negative sign. With a
1837 // positive sign, the solver will only stop when a solution has been found.
1839 -1 * solver_->time_limit_in_secs()));
1840 }
1841
1842 // Load basis if present
1843 // TODO : check number of variables / constraints
1844 if (!mMip && !initial_variables_basis_status_.empty() &&
1845 !initial_constraint_basis_status_.empty()) {
1846 CHECK_STATUS(XPRSloadbasis(mLp, initial_constraint_basis_status_.data(),
1847 initial_variables_basis_status_.data()));
1848 }
1849
1850 // Set the hint (if any)
1851 this->AddSolutionHintToOptimizer();
1852
1853 // Add opt node callback to optimizer. We have to do this here (just before
1854 // solve) to make sure the variables are fully initialized
1855 MPCallbackWrapper* mp_callback_wrapper = nullptr;
1856 if (callback_ != nullptr) {
1857 mp_callback_wrapper = new MPCallbackWrapper(callback_);
1859 static_cast<void*>(mp_callback_wrapper), 0));
1860 }
1861
1862 // Solve.
1863 // Do not CHECK_STATUS here since some errors (for example CPXERR_NO_MEMORY)
1864 // still allow us to query useful information.
1865 timer.Restart();
1866
1867 int xpress_stat = 0;
1868 if (mMip) {
1869 status = XPRSmipoptimize(mLp, "");
1870 XPRSgetintattrib(mLp, XPRS_MIPSTATUS, &xpress_stat);
1871 } else {
1872 status = XPRSlpoptimize(mLp, "");
1873 XPRSgetintattrib(mLp, XPRS_LPSTATUS, &xpress_stat);
1874 }
1875
1876 if (mp_callback_wrapper != nullptr) {
1877 mp_callback_wrapper->LogCaughtExceptions();
1878 delete mp_callback_wrapper;
1879 }
1880
1881 if (!(mMip ? (xpress_stat == XPRS_MIP_OPTIMAL)
1882 : (xpress_stat == XPRS_LP_OPTIMAL))) {
1883 XPRSpostsolve(mLp);
1884 }
1885
1886 // Disable screen output right after solve
1888
1889 if (status) {
1890 VLOG(1) << absl::StrFormat("Failed to optimize MIP. Error %d", status);
1891 // NOTE: We do not return immediately since there may be information
1892 // to grab (for example an incumbent)
1893 } else {
1894 VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get());
1895 }
1896
1897 VLOG(1) << absl::StrFormat("XPRESS solution status %d.", xpress_stat);
1898
1899 // Figure out what solution we have.
1900 bool const feasible = (mMip ? (xpress_stat == XPRS_MIP_OPTIMAL ||
1901 xpress_stat == XPRS_MIP_SOLUTION)
1902 : (!mMip && xpress_stat == XPRS_LP_OPTIMAL));
1903
1904 // Get problem dimensions for solution queries below.
1905 int const rows = getnumrows(mLp);
1906 int const cols = getnumcols(mLp);
1907 DCHECK_EQ(rows, solver_->constraints_.size());
1908 DCHECK_EQ(cols, solver_->variables_.size());
1909
1910 // Capture objective function value.
1913 if (feasible) {
1914 if (mMip) {
1918 } else {
1920 }
1921 }
1922 VLOG(1) << "objective=" << objective_value_
1923 << ", bound=" << best_objective_bound_;
1924
1925 // Capture primal and dual solutions
1926 if (mMip) {
1927 // If there is a primal feasible solution then capture it.
1928 if (feasible) {
1929 if (cols > 0) {
1930 unique_ptr<double[]> x(new double[cols]);
1931 CHECK_STATUS(XPRSgetmipsol(mLp, x.get(), 0));
1932 for (int i = 0; i < solver_->variables_.size(); ++i) {
1933 MPVariable* const var = solver_->variables_[i];
1934 var->set_solution_value(x[i]);
1935 VLOG(3) << var->name() << ": value =" << x[i];
1936 }
1937 }
1938 } else {
1939 for (auto& variable : solver_->variables_)
1940 variable->set_solution_value(XPRS_NAN);
1941 }
1942
1943 // MIP does not have duals
1944 for (auto& variable : solver_->variables_)
1945 variable->set_reduced_cost(XPRS_NAN);
1946 for (auto& constraint : solver_->constraints_)
1947 constraint->set_dual_value(XPRS_NAN);
1948 } else {
1949 // Continuous problem.
1950 if (cols > 0) {
1951 unique_ptr<double[]> x(new double[cols]);
1952 unique_ptr<double[]> dj(new double[cols]);
1953 if (feasible) CHECK_STATUS(XPRSgetlpsol(mLp, x.get(), 0, 0, dj.get()));
1954 for (int i = 0; i < solver_->variables_.size(); ++i) {
1955 MPVariable* const var = solver_->variables_[i];
1956 var->set_solution_value(x[i]);
1957 bool value = false, dual = false;
1958
1959 if (feasible) {
1960 var->set_solution_value(x[i]);
1961 value = true;
1962 } else {
1964 }
1965 if (feasible) {
1966 var->set_reduced_cost(dj[i]);
1967 dual = true;
1968 } else {
1970 }
1971 VLOG(3) << var->name() << ":"
1972 << (value ? absl::StrFormat(" value = %f", x[i]) : "")
1973 << (dual ? absl::StrFormat(" reduced cost = %f", dj[i]) : "");
1974 }
1975 }
1976
1977 if (rows > 0) {
1978 unique_ptr<double[]> pi(new double[rows]);
1979 if (feasible) {
1980 CHECK_STATUS(XPRSgetlpsol(mLp, 0, 0, pi.get(), 0));
1981 }
1982 for (int i = 0; i < solver_->constraints_.size(); ++i) {
1983 MPConstraint* const ct = solver_->constraints_[i];
1984 bool dual = false;
1985 if (feasible) {
1986 ct->set_dual_value(pi[i]);
1987 dual = true;
1988 } else {
1990 }
1991 VLOG(4) << "row " << ct->index() << ":"
1992 << (dual ? absl::StrFormat(" dual = %f", pi[i]) : "");
1993 }
1994 }
1995 }
1996
1997 // Map XPRESS status to more generic solution status in MPSolver
1998 if (mMip) {
1999 switch (xpress_stat) {
2000 case XPRS_MIP_OPTIMAL:
2002 break;
2003 case XPRS_MIP_INFEAS:
2005 break;
2006 case XPRS_MIP_UNBOUNDED:
2008 break;
2009 default:
2011 break;
2012 }
2013 } else {
2014 switch (xpress_stat) {
2015 case XPRS_LP_OPTIMAL:
2017 break;
2018 case XPRS_LP_INFEAS:
2020 break;
2021 case XPRS_LP_UNBOUNDED:
2023 break;
2024 default:
2026 break;
2027 }
2028 }
2029
2031 return result_status_;
2032}
2033
2034namespace {
2035template <class T>
2036struct getNameFlag;
2037
2038template <>
2039struct getNameFlag<MPVariable> {
2040 enum { value = XPRS_NAMES_COLUMN };
2041};
2042
2043template <>
2044struct getNameFlag<MPConstraint> {
2045 enum { value = XPRS_NAMES_ROW };
2046};
2047
2048template <class T>
2049// T = MPVariable | MPConstraint
2050// or any class that has a public method name() const
2051void ExtractNames(XPRSprob mLp, const std::vector<T*>& objects) {
2052 const bool have_names =
2053 std::any_of(objects.begin(), objects.end(),
2054 [](const T* x) { return !x->name().empty(); });
2055
2056 // FICO XPRESS requires a single large const char* such as
2057 // "name1\0name2\0name3"
2058 // See
2059 // https://www.fico.com/fico-xpress-optimization/docs/latest/solver/optimizer/HTML/XPRSaddnames.html
2060 if (have_names) {
2061 std::vector<char> all_names;
2062 for (const auto& x : objects) {
2063 const std::string& current_name = x->name();
2064 std::copy(current_name.begin(), current_name.end(),
2065 std::back_inserter(all_names));
2066 all_names.push_back('\0');
2067 }
2068
2069 // Remove trailing '\0', if any
2070 // Note : Calling pop_back on an empty container is undefined behavior.
2071 if (!all_names.empty() && all_names.back() == '\0') all_names.pop_back();
2072
2073 CHECK_STATUS(XPRSaddnames(mLp, getNameFlag<T>::value, all_names.data(), 0,
2074 objects.size() - 1));
2075 }
2076}
2077} // namespace
2078
2079void XpressInterface::Write(const std::string& filename) {
2080 if (sync_status_ == MUST_RELOAD) {
2081 Reset();
2082 }
2083 ExtractModel();
2084
2085 ExtractNames(mLp, solver_->variables_);
2086 ExtractNames(mLp, solver_->constraints_);
2087
2088 VLOG(1) << "Writing Xpress MPS \"" << filename << "\".";
2089 const int status = XPRSwriteprob(mLp, filename.c_str(), "");
2090 if (status) {
2091 LOG(ERROR) << "Xpress: Failed to write MPS!";
2092 }
2093}
2094
2096 return new XpressInterface(solver, mip);
2097}
2098// TODO useless ?
2099template <class Container>
2100void splitMyString(const std::string& str, Container& cont, char delim = ' ') {
2101 std::stringstream ss(str);
2102 std::string token;
2103 while (std::getline(ss, token, delim)) {
2104 cont.push_back(token);
2105 }
2106}
2107
2108bool stringToCharPtr(const std::string& var, const char** out) {
2109 *out = var.c_str();
2110 return true;
2111}
2112
2113#define setParamIfPossible_MACRO(target_map, setter, converter, type) \
2114 { \
2115 auto matchingParamIter = (target_map).find(paramAndValuePair.first); \
2116 if (matchingParamIter != (target_map).end()) { \
2117 type convertedValue; \
2118 bool ret = converter(paramAndValuePair.second, &convertedValue); \
2119 if (ret) { \
2120 VLOG(1) << "Setting parameter " << paramAndValuePair.first \
2121 << " to value " << convertedValue << std::endl; \
2122 } \
2123 setter(mLp, matchingParamIter->second, convertedValue); \
2124 continue; \
2125 } \
2126 }
2127
2128bool XpressInterface::SetSolverSpecificParametersAsString(
2129 const std::string& parameters) {
2130 if (parameters.empty()) return true;
2131
2132 std::vector<std::pair<std::string, std::string> > paramAndValuePairList;
2133
2134 std::stringstream ss(parameters);
2135 std::string paramName;
2136 while (std::getline(ss, paramName, ' ')) {
2137 std::string paramValue;
2138 if (std::getline(ss, paramValue, ' ')) {
2139 paramAndValuePairList.push_back(std::make_pair(paramName, paramValue));
2140 } else {
2141 LOG(ERROR) << "No value for parameter " << paramName << " : function "
2142 << __FUNCTION__ << std::endl;
2143 return false;
2144 }
2145 }
2146
2147 for (auto& paramAndValuePair : paramAndValuePairList) {
2148 setParamIfPossible_MACRO(mapIntegerControls_, XPRSsetintcontrol,
2149 absl::SimpleAtoi<int>, int);
2150 setParamIfPossible_MACRO(mapDoubleControls_, XPRSsetdblcontrol,
2151 absl::SimpleAtod, double);
2152 setParamIfPossible_MACRO(mapStringControls_, XPRSsetstrcontrol,
2153 stringToCharPtr, const char*);
2154 setParamIfPossible_MACRO(mapInteger64Controls_, XPRSsetintcontrol64,
2155 absl::SimpleAtoi<int64_t>, int64_t);
2156 LOG(ERROR) << "Unknown parameter " << paramName << " : function "
2157 << __FUNCTION__ << std::endl;
2158 return false;
2159 }
2160 return true;
2161}
2162
2163/*****************************************************************************\
2164* Name: optimizermsg
2165* Purpose: Display Optimizer error messages and warnings.
2166* Arguments: const char *sMsg Message string
2167* int nLen Message length
2168* int nMsgLvl Message type
2169* Return Value: None
2170\*****************************************************************************/
2171void XPRS_CC optimizermsg(XPRSprob prob, void* data, const char* sMsg, int nLen,
2172 int nMsgLvl) {
2173 auto* xprs = reinterpret_cast<operations_research::XpressInterface*>(data);
2174 if (!xprs->quiet()) {
2175 switch (nMsgLvl) {
2176 /* Print Optimizer error messages and warnings */
2177 case 4: /* error */
2178 case 3: /* warning */
2179 /* Ignore other messages */
2180 case 2: /* dialogue */
2181 case 1: /* information */
2182 printf("%*s\n", nLen, sMsg);
2183 break;
2184 /* Exit and flush buffers */
2185 default:
2186 fflush(nullptr);
2187 break;
2188 }
2189 }
2190}
2191
2192void XpressInterface::AddSolutionHintToOptimizer() {
2193 // Currently the XPRESS API does not handle clearing out previous hints
2194 const std::size_t len = solver_->solution_hint_.size();
2195 if (len == 0) {
2196 // hint is empty, nothing to do
2197 return;
2198 }
2199 unique_ptr<int[]> col_ind(new int[len]);
2200 unique_ptr<double[]> val(new double[len]);
2201
2202 for (std::size_t i = 0; i < len; ++i) {
2203 col_ind[i] = solver_->solution_hint_[i].first->index();
2204 val[i] = solver_->solution_hint_[i].second;
2205 }
2206 addhint(mLp, len, val.get(), col_ind.get());
2207}
2208
2210 if (callback_ != nullptr) {
2211 // replace existing callback by removing it first
2213 }
2214 callback_ = mp_callback;
2215}
2216
2217// This is the call-back called by XPRESS when it finds a new MIP solution
2218// NOTE(user): This function must have this exact API, because we are passing
2219// it to XPRESS as a callback.
2220void XPRS_CC XpressIntSolCallbackImpl(XPRSprob cbprob, void* cbdata) {
2221 auto callback_with_context = static_cast<MPCallbackWrapper*>(cbdata);
2222 if (callback_with_context == nullptr ||
2223 callback_with_context->GetCallback() == nullptr) {
2224 // nothing to do
2225 return;
2226 }
2227 try {
2228 std::unique_ptr<XpressMPCallbackContext> cb_context =
2229 std::make_unique<XpressMPCallbackContext>(
2230 &cbprob, MPCallbackEvent::kMipSolution, getnodecnt(cbprob));
2231 callback_with_context->GetCallback()->RunCallback(cb_context.get());
2232 } catch (std::exception&) {
2233 callback_with_context->CatchException(cbprob);
2234 }
2235}
2236
2240
2242 if (variable_values_.empty()) {
2243 int num_vars = getnumcols(*xprsprob_);
2244 variable_values_.resize(num_vars);
2245 CHECK_STATUS(XPRSgetmipsol(*xprsprob_, variable_values_.data(), 0));
2246 }
2247 return variable_values_[variable->index()];
2248}
2249
2251 const absl::flat_hash_map<const MPVariable*, double>& solution) {
2252 // Currently the XPRESS API does not handle clearing out previous hints
2253 const std::size_t len = solution.size();
2254 if (len == 0) {
2255 // hint is empty, do nothing
2256 return NAN;
2257 }
2259 // Currently, XPRESS does not handle adding a new MIP solution inside the
2260 // "cbintsol" callback (cb for new MIP solutions that is used here)
2261 // So we have to prevent the user from adding a solution
2262 // TODO: remove this workaround when it is handled in XPRESS
2263 LOG(WARNING)
2264 << "XPRESS does not currently allow suggesting MIP solutions after "
2265 "a kMipSolution event. Try another call-back.";
2266 return NAN;
2267 }
2268 unique_ptr<int[]> colind(new int[len]);
2269 unique_ptr<double[]> val(new double[len]);
2270 int i = 0;
2271 for (const auto& [var, value] : solution) {
2272 colind[i] = var->index();
2273 val[i] = value;
2274 ++i;
2275 }
2276 addhint(*xprsprob_, len, val.get(), colind.get());
2277
2278 // XPRESS doesn't guarantee if nor when it will test the suggested solution.
2279 // So we return NaN because we can't know the actual objective value.
2280 return NAN;
2281}
2282
2283} // namespace operations_research
double Get() const
Definition timer.h:44
void Restart()
Definition timer.h:34
void Start()
When Start() is called multiple times, only the most recent is used.
Definition timer.h:30
Wraps the MPCallback in order to catch and store exceptions.
void set_dual_value(double dual_value)
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
void set_variable_as_extracted(int var_index, bool extracted)
static constexpr int64_t kUnknownNumberOfIterations
friend class MPConstraint
To access the maximize_ bool and the MPSolver.
void set_constraint_as_extracted(int ct_index, bool extracted)
void ResetExtractionInformation()
Resets the extraction information.
int last_variable_index_
Index in MPSolver::constraints_ of last variable extracted.
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
Sets a supported integer parameter to an unsupported value.
int last_constraint_index_
Index in MPSolver::variables_ of last constraint extracted.
bool variable_is_extracted(int var_index) const
bool quiet() const
Returns the boolean indicating the verbosity of the solver output.
bool constraint_is_extracted(int ct_index) const
static constexpr int64_t kUnknownNumberOfNodes
void ExtractModel()
Extracts model stored in MPSolver.
double objective_value_
The value of the objective function.
double best_objective_bound_
The value of the best objective bound. Used only for MIP solvers.
bool maximize_
Optimization direction.
void SetMIPParameters(const MPSolverParameters &param)
Sets MIP specific parameters in the underlying solver.
void SetCommonParameters(const MPSolverParameters &param)
Sets parameters common to LP and MIP in the underlying solver.
SynchronizationStatus sync_status_
Indicates whether the model and the solution are synchronized.
PresolveValues
For each categorical parameter, enumeration of possible values.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
@ PRESOLVE
Advanced usage: presolve mode.
@ LP_ALGORITHM
Algorithm to solve linear programs.
@ SCALING
Advanced usage: enable or disable matrix scaling.
IncrementalityValues
Advanced usage: Incrementality options.
@ INCREMENTALITY_OFF
Start solve from scratch.
ScalingValues
Advanced usage: Scaling options.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
@ FEASIBLE
feasible, or stopped by limit.
@ INFEASIBLE
proven infeasible.
@ ABNORMAL
abnormal, i.e., error of some kind.
The class for variables of a Mathematical Programming (MP) model.
bool integer() const
Returns the integrality requirement of the variable.
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
const std::string & name() const
Returns the name of the variable.
void set_reduced_cost(double reduced_cost)
void set_solution_value(double value)
int index() const
Returns the index of the variable in the MPSolver::variables_.
MPSolver::BasisStatus row_status(int constraint_index) const override
Returns the basis status of a row.
virtual int64_t nodes() const
Number of branch-and-bound nodes. Only available for discrete problems.
MPSolver::BasisStatus column_status(int variable_index) const override
Returns the basis status of a column.
void ClearObjective() override
Clear the objective from all its terms.
void SetOptimizationDirection(bool maximize) override
Sets the optimization direction (min/max).
void AddRowConstraint(MPConstraint *ct) override
Adds a linear constraint.
void SetObjectiveOffset(double value) override
Change the constant term in the linear objective.
double ComputeExactConditionNumber() const override
bool IsLP() const override
Returns true if the problem is continuous and linear.
virtual bool ReadParameterFile(std::string const &filename)
void Reset() override
---— Model modifications and extraction --—
void SetCoefficient(MPConstraint *constraint, MPVariable const *variable, double new_value, double old_value) override
Changes a coefficient in a constraint.
void SetVariableBounds(int var_index, double lb, double ub) override
Modifies bounds of an extracted variable.
XpressInterface(MPSolver *solver, bool mip)
Creates an LP/MIP instance.
void Write(const std::string &filename) override
Writes the model.
void * underlying_solver() override
Returns the underlying solver.
MPSolver::ResultStatus Solve(MPSolverParameters const &param) override
void ExtractObjective() override
Extract the objective function.
void ClearConstraint(MPConstraint *constraint) override
Clear a constraint from all its terms.
void SetConstraintBounds(int row_index, double lb, double ub) override
Modify bounds of an extracted variable.
void SetRelativeMipGap(double value) override
Set each parameter in the underlying solver.
void SetDualTolerance(double value) override
void ExtractNewVariables() override
Extract all variables that have not yet been extracted.
void SetScalingMode(int value) override
Sets the scaling mode.
void AddVariable(MPVariable *var) override
Add a variable.
void SetObjectiveCoefficient(MPVariable const *variable, double coefficient) override
Change a coefficient in the linear objective.
void SetStartingLpBasis(const std::vector< MPSolver::BasisStatus > &variable_statuses, const std::vector< MPSolver::BasisStatus > &constraint_statuses) override
See MPSolver::SetStartingLpBasis().
void SetCallback(MPCallback *mp_callback) override
See MPSolver::SetCallback() for details.
virtual int64_t iterations() const
---— Query statistics on the solution and the solve ---—
void ExtractNewConstraints() override
Extract constraints that have not yet been extracted.
virtual std::string ValidFileExtensionForParameterFile() const
void SetPrimalTolerance(double value) override
void SetParameters(MPSolverParameters const &param) override
Set all parameters in the underlying solver.
void SetVariableInteger(int var_index, bool integer) override
Modifies integrality of an extracted variable.
bool IsMIP() const override
Returns true if the problem is discrete and linear.
bool IsContinuous() const override
--— Misc --—
std::string SolverVersion() const override
Returns a string describing the underlying solver and its version.
void AddCut(const LinearRange &cutting_plane) override
MPCallbackEvent Event() override
Implementation of the interface.
XpressMPCallbackContext(XPRSprob *xprsprob, MPCallbackEvent event, int num_nodes)
double SuggestSolution(const absl::flat_hash_map< const MPVariable *, double > &solution) override
void AddLazyConstraint(const LinearRange &lazy_constraint) override
double VariableValue(const MPVariable *variable) override
#define CHECK_STATUS(s)
In SWIG mode, we don't want anything besides these top-level includes.
std::function< int(XPRSprob prob, 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 attrib, int *p_value)> XPRSgetintattrib
static std::map< std::string, int > & getMapIntControls()
std::function< int(XPRSprob prob, int attrib, double *p_value)> XPRSgetdblattrib
std::function< int(XPRSprob prob, const char *filename, const char *flags)> XPRSwriteprob
std::function< int(XPRSprob prob, void(XPRS_CC *f_message)(XPRSprob cbprob, void *cbdata, const char *msg, int msglen, int msgtype), void *p, int priority)> XPRSaddcbmessage
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
static MPSolver::BasisStatus XpressToMPSolverBasisStatus(int xpress_basis_status)
Transform XPRESS basis status to MPSolver basis status.
bool initXpressEnv(bool verbose, int xpress_oem_license_key)
! init XPRESS environment.
std::function< int(XPRSprob prob, int length, const double solval[], const int colind[], const char *name)> XPRSaddmipsol
int getnumcols(const XPRSprob &mLp)
std::function< int(XPRSprob prob, double x[], double slack[])> XPRSgetmipsol
std::function< int(XPRSprob prob, int nbounds, const int colind[], const char bndtype[], const double bndval[])> XPRSchgbounds
int getnodecnt(const XPRSprob &mLp)
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
@ kMipSolution
Called every time a new MIP incumbent is found.
std::function< int(XPRSprob prob, int ncols, const int colind[], const char coltype[])> XPRSchgcoltype
std::function< int(XPRSprob prob, const char *name, int *p_id, int *p_type)> XPRSgetcontrolinfo
std::function< int(XPRSprob prob, double x[], double slack[], double duals[], double djs[])> XPRSgetlpsol
std::function< int(XPRSprob prob)> XPRSpostsolve
std::function< int(XPRSprob prob, int nrows, const int rowind[], const double rng[])> XPRSchgrhsrange
int setobjoffset(const XPRSprob &mLp, double value)
std::function< int(XPRSprob prob, int type, const char names[], int first, int last)> XPRSaddnames
static int MPSolverToXpressBasisStatus(MPSolver::BasisStatus mpsolver_basis_status)
Transform MPSolver basis status to XPRESS status.
std::function< int(XPRSprob prob, int ncoefs, const int rowind[], const int colind[], const double rowcoef[])> XPRSchgmcoef
std::function< int(void)> XPRSfree
std::function< int(XPRSprob prob, int reason)> XPRSinterrupt
int getitcnt(const XPRSprob &mLp)
std::vector< int > XpressBasisStatusesFrom(const std::vector< MPSolver::BasisStatus > &statuses)
MPSolverInterface * BuildXpressInterface(bool mip, MPSolver *const solver)
void splitMyString(const std::string &str, Container &cont, char delim=' ')
std::function< int(XPRSprob prob, int control, int value)> XPRSsetintcontrol
std::function< int(XPRSprob prob, int nrows, int ncoefs, const char rowtype[], const double rhs[], const double rng[], const int start[], const int colind[], const double rowcoef[])> XPRSaddrows
static std::map< std::string, int > & getMapStringControls()
std::function< int(XPRSprob prob, char *errmsg)> XPRSgetlasterror
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)
Apply the specified name=value setting to prob.
#define XPRS_COMPUTEEXECSERVICE
#define XPRS_INPUTTOL
#define XPRS_BARORDERTHREADS
#define XPRS_EXTRAMIPENTS
#define XPRS_PREPERMUTE
#define XPRS_MPSOBJNAME
#define XPRS_MAXMEMORYHARD
#define XPRS_HEURSEARCHTARGETSIZE
#define XPRS_RELAXTREEMEMORYLIMIT
#define XPRS_PRESOLVEOPS
#define XPRS_CROSSOVERRELPIVOTTOL
#define XPRS_BARGAPTARGET
#define XPRS_TREECUTSELECT
#define XPRS_SIMPLEXITER
#define XPRS_SBEFFORT
#define XPRS_TUNERTHREADS
#define XPRS_BARDUALSTOP
#define XPRS_MAXPAGELINES
#define XPRS_HEURMAXSOL
#define XPRS_L1CACHE
#define XPRS_MAXCHECKSONMAXTIME
#define XPRS_HEURDIVEITERLIMIT
#define XPRS_GLOBALBOUNDINGBOX
#define XPRS_FEASTOLPERTURB
#define XPRS_TREEFILELOGINTERVAL
#define XPRS_BAROBJSCALE
#define XPRS_HEURSEARCHTREESELECT
#define XPRS_RESOURCESTRATEGY
#define XPRS_INVERTMIN
#define XPRS_TREEMEMORYSAVINGTARGET
#define XPRS_ROWS
#define XPRS_QUADRATICUNSHIFT
#define XPRS_MIPABSGAPNOTIFYOBJ
#define XPRS_LPSTATUS
#define XPRS_CROSSOVERITERLIMIT
#define XPRS_TYPE_NOTDEFINED
Definition environment.h:84
#define XPRS_CLAMPING
#define XPRS_TYPE_INT
Definition environment.h:85
#define XPRS_CPIALPHA
#define XPRS_MIPKAPPAFREQ
#define XPRS_CROSSOVERFEASWEIGHT
#define XPRS_REFINEOPS
#define XPRS_BRANCHCHOICE
#define XPRS_OBJ_MAXIMIZE
#define XPRS_FEASIBILITYJUMP
#define XPRS_OUTPUTCONTROLS
#define XPRS_LP_INFEAS
#define XPRS_PREBASISRED
#define XPRS_INVERTFREQ
#define XPRS_COMPUTEMATX
#define XPRS_MIP_SOLUTION
#define XPRS_HEURDEPTH
#define XPRS_BARPRESOLVEOPS
#define XPRS_MIPABSGAPNOTIFY
#define XPRS_HEURSEARCHFREQ
#define XPRS_BARINDEFLIMIT
#define XPRS_SIFTPRESOLVEOPS
#define XPRS_HEURNODES
#define XPRS_VARSELECTION
#define XPRS_NAMES_ROW
Definition environment.h:92
#define XPRS_CHECKINPUTDATA
#define XPRS_PLUSINFINITY
Definition environment.h:95
#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
Definition environment.h:86
#define XPRS_SOLTIMELIMIT
#define XPRS_CROSSOVER
#define XPRS_USERSOLHEURISTIC
#define XPRS_BARFAILITERLIMIT
#define XPRS_PRECLIQUESTRATEGY
#define XPRS_FEASTOLTARGET
#define XPRS_BARCRASH
#define XPRS_MIPRELGAPNOTIFY
#define XPRSint64
Definition environment.h:56
#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
Copyright 2019-2023 RTE.
Definition environment.h:27
#define XPRS_HEURSEARCHROOTSELECT
#define XPRS_MIPDUALREDUCTIONS
#define XPRS_PRECOEFELIM
#define XPRS_PREELIMQUAD
#define XPRS_FEASTOL
#define XPRS_MAXMIPSOL
#define XPRS_DUALPERTURB
#define XPRS_BARNUMSTABILITY
#define XPRS_PRESOLVEPASSES
#define XPRS_DETLOGFREQ
#define XPRS_CHOLESKYTOL
#define XPRS_BRANCHDISJ
#define XPRS_MAXTIME
#define XPRS_HEURFREQ
#define XPRS_IGNORECONTAINERMEMORYLIMIT
#define XPRS_PIVOTTOL
#define XPRS_PRIMALUNSHIFT
#define XPRS_MPSFORMAT
#define XPRS_CONCURRENTTHREADS
#define XPRS_HEURSEARCHEFFORT
#define XPRS_MIQCPALG
#define XPRS_MAXIMPLIEDBOUND
#define XPRS_PREPROBING
#define XPRS_OUTPUTTOL
#define XPRS_SBITERLIMIT
#define XPRS_PRESOLVE
#define XPRS_TREEQCCUTS
#define XPRS_ROOTPRESOLVE
#define XPRS_MPSECHO
#define XPRS_CROSSOVERTHREADS
#define XPRS_GOMCUTS
#define XPRS_BESTBOUND
#define XPRS_AUTOPERTURB
#define XPRS_PREBNDREDCONE
#define XPRS_MAXCHECKSONMAXCUTTIME
#define XPRS_VERSION
#define XPRS_MUTEXCALLBACKS
#define XPRS_CACHESIZE
#define XPRS_CHOLESKYALG
#define XPRS_BARSTART
#define XPRS_INDLINBIGM
#define XPRS_PRESORT
#define XPRS_LPLOG
#define XPRS_SYMSELECT
#define XPRS_AT_LOWER
#define XPRS_ELIMFILLIN
#define XPRS_SBESTIMATE
#define XPRS_HEURSELECT
#define XPRS_MAXIIS
#define XPRS_CORESPERCPU
#define XPRS_SIFTSWITCH
#define XPRS_MIPRELCUTOFF
#define XPRS_MPS18COMPATIBLE
#define XPRS_BARREFITER
#define XPRS_CUTSELECT
#define XPRS_BARSTARTWEIGHT
#define XPRS_OPTIMALITYTOLTARGET
#define XPRS_PPFACTOR
#define XPRS_TYPE_DOUBLE
Definition environment.h:87
#define XPRS_PREANALYTICCENTER
#define XPRS_IOTIMEOUT
#define XPRS_HEURDIVESTRATEGY
#define XPRS_BREADTHFIRST
#define XPRS_CPUTIME
#define XPRS_MIPRAMPUP
#define XPRS_SBBEST
#define XPRS_LOCALBACKTRACK
#define XPRS_RELPIVOTTOL
#define XPRS_MIPPRESOLVE
#define XPRS_TUNERMAXTIME
#define XPRS_FEASIBILITYPUMP
#define XPRS_TUNERMODE
#define XPRS_MIPOBJVAL
#define XPRS_PREDOMROW
#define XPRS_REPAIRINDEFINITEQMAX
#define XPRS_TUNERHISTORY
#define XPRS_REPAIRINDEFINITEQ
#define XPRS_BARKERNEL
#define XPRS_TREEDIAGNOSTICS
#define XPRS_PRECONFIGURATION
#define XPRS_CROSSOVEROPS
#define XPRS_ETATOL
#define XPRS_MIPABSGAPNOTIFYBOUND
#define XPRS_COMPUTEMATX_IIS
#define XPRS_OUTPUTLOG
#define XPRS_ELIMTOL
#define XPRS_NODES
#define XPRS_LP_OPTIMAL
#define XPRS_CONFLICTCUTS
#define XPRS_INDPRELINBIGM
#define XPRS_LPFLAGS
#define XPRS_PREDUPROW
#define XPRS_COVERCUTS
#define XPRS_QCCUTS
#define XPRS_PRIMALPERTURB
#define XPRS_ALGAFTERNETWORK
#define XPRS_DUALGRADIENT
#define XPRS_PENALTY
#define XPRS_AUTOCUTTING
#define XPRS_COMPUTELOG
#define XPRS_PRECOMPONENTS
#define XPRS_NODEPROBINGEFFORT
#define XPRS_PWLNONCONVEXTRANSFORMATION
#define XPRS_TREECOMPRESSION
#define XPRS_THREADS
#define XPRS_SYMMETRY
#define XPRS_FORCEOUTPUT
#define XPRS_TUNERMETHOD
#define XPRS_BARSTEPSTOP
#define XPRS_CUTSTRATEGY
#define XPRS_REFACTOR
#define XPRS_AUTOSCALING
#define XPRS_BARALG
#define XPRS_PRESOLVEMAXGROW
#define XPRS_TREEMEMORYLIMIT
#define XPRS_CUTFREQ
#define XPRS_MIPCOMPONENTS
#define XPRS_LPITERLIMIT
#define XPRS_TUNERSESSIONNAME
#define XPRS_BARORDER
#define XPRS_BARFREESCALE
#define XPRS_TUNEROUTPUTPATH
#define XPRS_MARKOWITZTOL
#define XPRS_HEURDIVERANDOMIZE
#define XPRS_PWLDUALREDUCTIONS
#define XPRS_AT_UPPER
#define XPRS_OUTPUTMASK
#define XPRS_DUALIZEOPS
#define XPRS_BARLARGEBOUND
#define XPRS_OPTIMALITYTOL
#define XPRS_KEEPNROWS
#define XPRS_MAXSCALEFACTOR
#define XPRS_CROSSOVERACCURACYTOL
#define XPRS_PRECONEDECOMP
#define XPRS_DUALTHREADS
#define XPRS_HEUREMPHASIS
#define XPRS_BARITERLIMIT
#define XPRS_KEEPBASIS
#define XPRS_BIGMMETHOD
#define XPRS_TUNERVERBOSE
#define XPRS_TUNERPERMUTE
#define XPRS_PREOBJCUTDETECT
#define XPRS_LOCALCHOICE
#define XPRS_LNPBEST
#define XPRS_PREPROTECTDUAL
#define XPRS_REPAIRINFEASTIMELIMIT
#define XPRS_MPSNAMELENGTH
#define XPRS_LPFOLDING
#define XPRS_GENCONSDUALREDUCTIONS
#define XPRS_LP_UNBOUNDED
#define XPRS_MATRIXTOL
#define XPRS_GENCONSABSTRANSFORMATION
#define XPRS_LPREFINEITERLIMIT
#define XPRS_PREDOMCOL
#define XPRS_HEURDIVESOFTROUNDING
#define XPRS_TUNEROUTPUT
#define XPRS_MIPREFINEITERLIMIT
#define XPRS_BARPERTURB
#define XPRS_REPAIRINFEASMAXTIME
#define XPRS_MAXTREEFILESIZE
#define XPRS_MIPRESTART
#define XPRS_HEURBEFORELP
#define XPRS_STOP_USER
Definition environment.h:77
#define XPRS_CALLBACKCHECKTIMEDELAY
#define XPRS_DEFAULTALG
#define XPRS_PRELINDEP
#define XPRS_EXTRASETS
#define XPRS_TIMELIMIT
#define XPRS_BARRHSSCALE
#define XPRS_LNPITERLIMIT
#define XPRS_BARREGULARIZE
#define XPRS_NODESELECTION
#define XPRS_BIGM
#define XPRS_MIPLOG
#define XPRS_SIFTPASSES
#define XPRS_IGNORECONTAINERCPULIMIT
#define XPRS_CALLBACKFROMMASTERTHREAD
#define XPRS_LPOBJVAL
#define XPRS_EXTRAROWS
#define XPRS_EXTRACOLS
#define XPRS_CUTFACTOR
#define XPRS_ESCAPENAMES
#define XPRS_MAXNODE
#define XPRS_MIPCONCURRENTNODES
#define XPRS_SIFTING
#define XPRS_EXTRASETELEMS
#define XPRS_EXTRAELEMS
#define XPRS_CUTDEPTH
#define XPRS_MIPABSCUTOFF
#define XPRS_TREECOVERCUTS
#define XPRS_DENSECOLLIMIT
#define XPRS_SLEEPONTHREADWAIT
#define XPRS_GLOBALSPATIALBRANCHIFPREFERORIG
#define XPRS_NAMES_COLUMN
Definition environment.h:93
#define XPRS_FORCEPARALLELDUAL
#define XPRS_MIP_INFEAS
#define XPRS_MIPCONCURRENTSOLVES
#define XPRS_MPSRHSNAME
Definition environment.h:99
#define XPRS_CROSSOVERDRP
#define XPRS_MULTIOBJOPS
#define XPRS_MIPRESTARTGAPTHRESHOLD
#define XPRS_MINUSINFINITY
Definition environment.h:96
#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
NOLINTEND(runtime/int)
Definition environment.h:63
#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
Initial version of this code was provided by RTE.