Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
g_xpress.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <memory>
17#include <optional>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "absl/log/check.h"
23#include "absl/log/die_if_null.h"
24#include "absl/memory/memory.h"
25#include "absl/status/status.h"
26#include "absl/status/statusor.h"
27#include "absl/strings/str_format.h"
28#include "absl/types/span.h"
34
36
37namespace {
38bool checkInt32Overflow(const unsigned int value) { return value > INT32_MAX; }
39} // namespace
40
41constexpr int kXpressOk = 0;
42
43absl::Status Xpress::ToStatus(const int xprs_err,
44 const absl::StatusCode code) const {
45 if (xprs_err == kXpressOk) {
46 return absl::OkStatus();
47 }
48 char errmsg[512];
49 int status = XPRSgetlasterror(xpress_model_, errmsg);
50 if (status == kXpressOk) {
51 return util::StatusBuilder(code)
52 << "Xpress error code: " << xprs_err << ", message: " << errmsg;
53 }
54 return util::StatusBuilder(code) << "Xpress error code: " << xprs_err
55 << " (message could not be fetched)";
56}
57
58Xpress::Xpress(XPRSprob& model) : xpress_model_(ABSL_DIE_IF_NULL(model)) {
59 initIntControlDefaults();
60}
61
62absl::StatusOr<std::unique_ptr<Xpress>> Xpress::New(
63 const std::string& model_name) {
64 bool correctlyLoaded = initXpressEnv();
65 CHECK(correctlyLoaded);
66 XPRSprob model;
67 CHECK_EQ(kXpressOk, XPRScreateprob(&model));
68 CHECK_EQ(kXpressOk, XPRSaddcbmessage(model, printXpressMessage, nullptr, 0));
69 return absl::WrapUnique(new Xpress(model));
70}
71
72absl::Status Xpress::SetProbName(const std::string& name) {
73 std::string truncated = name;
74 auto maxLength = GetIntAttr(XPRS_MAXPROBNAMELENGTH);
75 if (truncated.length() > maxLength.value_or(INT_MAX)) {
76 truncated = truncated.substr(0, maxLength.value_or(INT_MAX));
77 }
78 return ToStatus(XPRSsetprobname(xpress_model_, truncated.c_str()));
79}
80
82 const char* sMsg, int nLen,
83 int nMsgLvl) {
84 if (sMsg) {
85 std::cout << sMsg << std::endl;
86 }
87}
88
90 CHECK_EQ(kXpressOk, XPRSdestroyprob(xpress_model_));
91 CHECK_EQ(kXpressOk, XPRSfree());
92}
93
94void Xpress::initIntControlDefaults() {
95 std::vector controls = {XPRS_LPITERLIMIT, XPRS_BARITERLIMIT};
96 for (auto control : controls) {
97 int_control_defaults_[control] = GetIntControl(control).value();
98 }
99}
100
101absl::Status Xpress::AddVars(const absl::Span<const double> obj,
102 const absl::Span<const double> lb,
103 const absl::Span<const double> ub,
104 const absl::Span<const char> vtype) {
105 return AddVars({}, {}, {}, obj, lb, ub, vtype);
106}
107
108absl::Status Xpress::AddVars(const absl::Span<const int> vbegin,
109 const absl::Span<const int> vind,
110 const absl::Span<const double> vval,
111 const absl::Span<const double> obj,
112 const absl::Span<const double> lb,
113 const absl::Span<const double> ub,
114 const absl::Span<const char> vtype) {
115 if (checkInt32Overflow(lb.size())) {
116 return absl::InvalidArgumentError(
117 "XPRESS cannot handle more than 2^31 variables");
118 }
119 const int num_vars = static_cast<int>(lb.size());
120 if (vind.size() != vval.size() || ub.size() != num_vars ||
121 vtype.size() != num_vars || (!obj.empty() && obj.size() != num_vars) ||
122 (!vbegin.empty() && vbegin.size() != num_vars)) {
123 return absl::InvalidArgumentError(
124 "Xpress::AddVars arguments are of inconsistent sizes");
125 }
126 double* c_obj = nullptr;
127 if (!obj.empty()) {
128 c_obj = const_cast<double*>(obj.data());
129 }
130 // TODO: look into int64 support for number of vars (use XPRSaddcols64)
131 return ToStatus(XPRSaddcols(xpress_model_, num_vars, 0, c_obj, nullptr,
132 nullptr, nullptr, lb.data(), ub.data()));
133}
134
135absl::Status Xpress::AddConstrs(const absl::Span<const char> sense,
136 const absl::Span<const double> rhs,
137 const absl::Span<const double> rng) {
138 const int num_cons = static_cast<int>(sense.size());
139 if (rhs.size() != num_cons) {
140 return absl::InvalidArgumentError(
141 "RHS must have one element per constraint.");
142 }
143 return ToStatus(XPRSaddrows(xpress_model_, num_cons, 0, sense.data(),
144 rhs.data(), rng.data(), NULL, NULL, NULL));
145}
146
147absl::Status Xpress::AddConstrs(const absl::Span<const char> rowtype,
148 const absl::Span<const double> rhs,
149 const absl::Span<const double> rng,
150 const absl::Span<const int> start,
151 const absl::Span<const int> colind,
152 const absl::Span<const double> rowcoef) {
153 const int num_cons = static_cast<int>(rowtype.size());
154 if (rhs.size() != num_cons) {
155 return absl::InvalidArgumentError(
156 "RHS must have one element per constraint.");
157 }
158 if (start.size() != num_cons) {
159 return absl::InvalidArgumentError(
160 "START must have one element per constraint.");
161 }
162 if (colind.size() != rowcoef.size()) {
163 return absl::InvalidArgumentError(
164 "COLIND and ROWCOEF must be of the same size.");
165 }
166 return ToStatus(XPRSaddrows(xpress_model_, num_cons, 0, rowtype.data(),
167 rhs.data(), rng.data(), start.data(),
168 colind.data(), rowcoef.data()));
169}
170
171absl::Status Xpress::SetObjectiveSense(bool maximize) {
172 return ToStatus(XPRSchgobjsense(
173 xpress_model_, maximize ? XPRS_OBJ_MAXIMIZE : XPRS_OBJ_MINIMIZE));
174}
175
177 double offset, const absl::Span<const int> colind,
178 const absl::Span<const double> coefficients) {
179 static int indexes[1] = {-1};
180 double xprs_values[1] = {-offset};
181 RETURN_IF_ERROR(ToStatus(XPRSchgobj(xpress_model_, 1, indexes, xprs_values)))
182 << "Failed to set objective offset in XPRESS";
183
184 const int n_cols = static_cast<int>(colind.size());
185 return ToStatus(
186 XPRSchgobj(xpress_model_, n_cols, colind.data(), coefficients.data()));
187}
188
190 const absl::Span<const int> colind1, const absl::Span<const int> colind2,
191 const absl::Span<const double> coefficients) {
192 const int ncoefs = static_cast<int>(coefficients.size());
193 return ToStatus(XPRSchgmqobj(xpress_model_, ncoefs, colind1.data(),
194 colind2.data(), coefficients.data()));
195}
196
197absl::Status Xpress::ChgCoeffs(absl::Span<const int> rowind,
198 absl::Span<const int> colind,
199 absl::Span<const double> values) {
200 const long n_coefs = static_cast<long>(rowind.size());
201 return ToStatus(XPRSchgmcoef64(xpress_model_, n_coefs, rowind.data(),
202 colind.data(), values.data()));
203}
204
205absl::Status Xpress::LpOptimize(std::string flags) {
206 return ToStatus(XPRSlpoptimize(xpress_model_, flags.c_str()));
207}
208
209absl::Status Xpress::GetLpSol(absl::Span<double> primals,
210 absl::Span<double> duals,
211 absl::Span<double> reducedCosts) {
212 return ToStatus(XPRSgetlpsol(xpress_model_, primals.data(), nullptr,
213 duals.data(), reducedCosts.data()));
214}
215
216absl::Status Xpress::PostSolve() {
217 return ToStatus(XPRSpostsolve(xpress_model_));
218}
219
220absl::Status Xpress::MipOptimize() {
221 return ToStatus(XPRSmipoptimize(xpress_model_, nullptr));
222}
223
225
226absl::StatusOr<int> Xpress::GetIntControl(int control) const {
227 int result;
228 RETURN_IF_ERROR(ToStatus(XPRSgetintcontrol(xpress_model_, control, &result)))
229 << "Error getting Xpress int control: " << control;
230 return result;
231}
232
233absl::Status Xpress::SetIntControl(int control, int value) {
234 return ToStatus(XPRSsetintcontrol(xpress_model_, control, value));
235}
236
237absl::Status Xpress::ResetIntControl(int control) {
238 if (int_control_defaults_.count(control)) {
239 return ToStatus(XPRSsetintcontrol(xpress_model_, control,
240 int_control_defaults_[control]));
241 }
242 return absl::InvalidArgumentError(
243 "Default value unknown for control " + std::to_string(control) +
244 ", consider adding it to Xpress::initIntControlDefaults");
245}
246
247absl::StatusOr<int> Xpress::GetIntAttr(int attribute) const {
248 int result;
249 RETURN_IF_ERROR(ToStatus(XPRSgetintattrib(xpress_model_, attribute, &result)))
250 << "Error getting Xpress int attribute: " << attribute;
251 return result;
252}
253
254absl::StatusOr<double> Xpress::GetDoubleAttr(int attribute) const {
255 double result;
256 RETURN_IF_ERROR(ToStatus(XPRSgetdblattrib(xpress_model_, attribute, &result)))
257 << "Error getting Xpress double attribute: " << attribute;
258 return result;
259}
260
262 int n;
263 XPRSgetintattrib(xpress_model_, XPRS_ROWS, &n);
264 return n;
265}
266
268 int n;
269 XPRSgetintattrib(xpress_model_, XPRS_COLS, &n);
270 return n;
271}
272
273absl::StatusOr<int> Xpress::GetDualStatus() const {
274 int status = 0;
275 double values[1];
276 // Even though we do not need the values, we have to fetch them, otherwise
277 // we'd get a segmentation fault
278 RETURN_IF_ERROR(ToStatus(XPRSgetduals(xpress_model_, &status, values, 0, 0)))
279 << "Failed to retrieve dual status from XPRESS";
280 return status;
281}
282
283absl::Status Xpress::GetBasis(std::vector<int>& rowBasis,
284 std::vector<int>& colBasis) const {
285 rowBasis.resize(GetNumberOfConstraints());
286 colBasis.resize(GetNumberOfVariables());
287 return ToStatus(
288 XPRSgetbasis(xpress_model_, rowBasis.data(), colBasis.data()));
289}
290
291absl::Status Xpress::SetStartingBasis(std::vector<int>& rowBasis,
292 std::vector<int>& colBasis) const {
293 if (rowBasis.size() != colBasis.size()) {
294 return absl::InvalidArgumentError(
295 "Row basis and column basis must be of same size.");
296 }
297 return ToStatus(
298 XPRSloadbasis(xpress_model_, rowBasis.data(), colBasis.data()));
299}
300
301absl::StatusOr<std::vector<double>> Xpress::GetVarLb() const {
302 int nVars = GetNumberOfVariables();
303 std::vector<double> bounds;
304 bounds.reserve(nVars);
306 ToStatus(XPRSgetlb(xpress_model_, bounds.data(), 0, nVars - 1)))
307 << "Failed to retrieve variable LB from XPRESS";
308 return bounds;
309}
310absl::StatusOr<std::vector<double>> Xpress::GetVarUb() const {
311 int nVars = GetNumberOfVariables();
312 std::vector<double> bounds;
313 bounds.reserve(nVars);
315 ToStatus(XPRSgetub(xpress_model_, bounds.data(), 0, nVars - 1)))
316 << "Failed to retrieve variable UB from XPRESS";
317 return bounds;
318}
319
320} // namespace operations_research::math_opt
#define RETURN_IF_ERROR(expr)
static absl::StatusOr< std::unique_ptr< Xpress > > New(const std::string &model_name)
Creates a new Xpress.
Definition g_xpress.cc:62
absl::StatusOr< std::vector< double > > GetVarUb() const
Definition g_xpress.cc:310
absl::Status SetLinearObjective(double offset, absl::Span< const int > colind, absl::Span< const double > values)
Definition g_xpress.cc:176
absl::Status SetIntControl(int control, int value)
Definition g_xpress.cc:233
absl::Status SetStartingBasis(std::vector< int > &rowBasis, std::vector< int > &colBasis) const
Definition g_xpress.cc:291
absl::StatusOr< int > GetDualStatus() const
Definition g_xpress.cc:273
absl::StatusOr< int > GetIntAttr(int attribute) const
Definition g_xpress.cc:247
absl::Status GetLpSol(absl::Span< double > primals, absl::Span< double > duals, absl::Span< double > reducedCosts)
Definition g_xpress.cc:209
absl::Status AddConstrs(absl::Span< const char > sense, absl::Span< const double > rhs, absl::Span< const double > rng)
Definition g_xpress.cc:135
absl::Status AddVars(absl::Span< const double > obj, absl::Span< const double > lb, absl::Span< const double > ub, absl::Span< const char > vtype)
Definition g_xpress.cc:101
absl::Status ResetIntControl(int control)
Definition g_xpress.cc:237
absl::Status LpOptimize(std::string flags)
Definition g_xpress.cc:205
absl::Status SetQuadraticObjective(absl::Span< const int > colind1, absl::Span< const int > colind2, absl::Span< const double > coefficients)
Definition g_xpress.cc:189
absl::StatusOr< int > GetIntControl(int control) const
Definition g_xpress.cc:226
absl::StatusOr< double > GetDoubleAttr(int attribute) const
Definition g_xpress.cc:254
static void XPRS_CC printXpressMessage(XPRSprob prob, void *data, const char *sMsg, int nLen, int nMsgLvl)
Definition g_xpress.cc:81
absl::Status SetProbName(const std::string &name)
Definition g_xpress.cc:72
absl::Status GetBasis(std::vector< int > &rowBasis, std::vector< int > &colBasis) const
Definition g_xpress.cc:283
absl::Status SetObjectiveSense(bool maximize)
Definition g_xpress.cc:171
absl::StatusOr< std::vector< double > > GetVarLb() const
Definition g_xpress.cc:301
absl::Status ChgCoeffs(absl::Span< const int > cind, absl::Span< const int > vind, absl::Span< const double > val)
Definition g_xpress.cc:197
An object oriented wrapper for quadratic constraints in ModelStorage.
Definition gurobi_isv.cc:28
std::function< int(XPRSprob prob, int attrib, int *p_value)> XPRSgetintattrib
std::function< int(XPRSprob prob, int ncoefs, const int objqcol1[], const int objqcol2[], const double objqcoef[])> XPRSchgmqobj
std::function< int(XPRSprob prob, int attrib, double *p_value)> XPRSgetdblattrib
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
bool initXpressEnv(bool verbose, int xpress_oem_license_key)
! init XPRESS environment.
std::function< int(XPRSprob prob, const int rowstat[], const int colstat[])> XPRSloadbasis
std::function< int(XPRSprob prob, double lb[], int first, int last)> XPRSgetlb
std::function< int(XPRSprob prob, int control, int *p_value)> XPRSgetintcontrol
std::function< int(XPRSprob prob, double x[], double slack[], double duals[], double djs[])> XPRSgetlpsol
std::function< int(XPRSprob prob)> XPRSpostsolve
std::function< int(void)> XPRSfree
std::function< int(XPRSprob prob, int reason)> XPRSinterrupt
std::function< int(XPRSprob prob, int control, int value)> XPRSsetintcontrol
std::function< int(XPRSprob prob, int nrows, int ncoefs, const char rowtype[], const double rhs[], const double rng[], const int start[], const int colind[], const double rowcoef[])> XPRSaddrows
std::function< int(XPRSprob prob, char *errmsg)> XPRSgetlasterror
std::function< int(XPRSprob prob)> XPRSdestroyprob
std::function< int(XPRSprob prob, XPRSint64 ncoefs, const int rowind[], const int colind[], const double rowcoef[])> XPRSchgmcoef64
std::function< int(XPRSprob prob, double ub[], int first, int last)> XPRSgetub
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
std::function< int(XPRSprob prob, const char *flags)> XPRSmipoptimize
std::function< int(XPRSprob prob, int ncols, const int colind[], const double objcoef[])> XPRSchgobj
std::function< int(XPRSprob prob, int *status, double duals[], int first, int last)> XPRSgetduals
std::function< int(XPRSprob *p_prob)> XPRScreateprob
This is the 'define' section.
std::function< int(XPRSprob prob, const char *flags)> XPRSlpoptimize
std::function< int(XPRSprob prob, int objsense)> XPRSchgobjsense
std::function< int(XPRSprob prob, const char *probname)> XPRSsetprobname
std::function< int(XPRSprob prob, int rowstat[], int colstat[])> XPRSgetbasis
#define INT32_MAX
Definition parser.yy.cc:310
#define XPRS_ROWS
#define XPRS_OBJ_MAXIMIZE
#define XPRS_COLS
struct xo_prob_struct * XPRSprob
Initial version of this code was provided by RTE.
Definition environment.h:26
#define XPRS_MAXPROBNAMELENGTH
#define XPRS_LPITERLIMIT
#define XPRS_BARITERLIMIT
#define XPRS_STOP_USER
Definition environment.h:74
#define XPRS_CC
Definition environment.h:60
#define XPRS_OBJ_MINIMIZE