Google OR-Tools v9.14
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 <climits>
17#include <cstddef>
18#include <cstdint>
19#include <memory>
20#include <string>
21#include <vector>
22
23#include "absl/log/check.h"
24#include "absl/log/die_if_null.h"
25#include "absl/memory/memory.h"
26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/string_view.h"
29#include "absl/types/span.h"
34
36
37namespace {
38bool checkInt32Overflow(const std::size_t 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(absl::string_view) {
63 bool correctlyLoaded = initXpressEnv();
64 CHECK(correctlyLoaded);
65 XPRSprob model;
66 CHECK_EQ(kXpressOk, XPRScreateprob(&model));
67 CHECK_EQ(kXpressOk, XPRSaddcbmessage(model, printXpressMessage, nullptr, 0));
68 return absl::WrapUnique(new Xpress(model));
69}
70
71absl::Status Xpress::SetProbName(absl::string_view name) {
72 std::string truncated(name);
73 auto maxLength = GetIntAttr(XPRS_MAXPROBNAMELENGTH);
74 if (truncated.length() > maxLength.value_or(INT_MAX)) {
75 truncated = truncated.substr(0, maxLength.value_or(INT_MAX));
76 }
77 return ToStatus(XPRSsetprobname(xpress_model_, truncated.c_str()));
78}
79
80void XPRS_CC Xpress::printXpressMessage(XPRSprob, void*, const char* sMsg, int,
81 int) {
82 if (sMsg) {
83 LOG(INFO) << sMsg;
84 }
85}
86
88 CHECK_EQ(kXpressOk, XPRSdestroyprob(xpress_model_));
89 CHECK_EQ(kXpressOk, XPRSfree());
90}
91
92void Xpress::initIntControlDefaults() {
93 std::vector controls = {XPRS_LPITERLIMIT, XPRS_BARITERLIMIT};
94 for (auto control : controls) {
95 int_control_defaults_[control] = GetIntControl(control).value();
96 }
97}
98
99absl::Status Xpress::AddVars(const absl::Span<const double> obj,
100 const absl::Span<const double> lb,
101 const absl::Span<const double> ub,
102 const absl::Span<const char> vtype) {
103 return AddVars({}, {}, {}, obj, lb, ub, vtype);
104}
105
106absl::Status Xpress::AddVars(const absl::Span<const int> vbegin,
107 const absl::Span<const int> vind,
108 const absl::Span<const double> vval,
109 const absl::Span<const double> obj,
110 const absl::Span<const double> lb,
111 const absl::Span<const double> ub,
112 const absl::Span<const char> vtype) {
113 if (checkInt32Overflow(lb.size())) {
114 return absl::InvalidArgumentError(
115 "XPRESS cannot handle more than 2^31 variables");
116 }
117 const int num_vars = static_cast<int>(lb.size());
118 if (vind.size() != vval.size() || ub.size() != num_vars ||
119 vtype.size() != num_vars || (!obj.empty() && obj.size() != num_vars) ||
120 (!vbegin.empty() && vbegin.size() != num_vars)) {
121 return absl::InvalidArgumentError(
122 "Xpress::AddVars arguments are of inconsistent sizes");
123 }
124 double* c_obj = nullptr;
125 if (!obj.empty()) {
126 c_obj = const_cast<double*>(obj.data());
127 }
128 // TODO: look into int64_t support for number of vars (use XPRSaddcols64)
129 return ToStatus(XPRSaddcols(xpress_model_, num_vars, 0, c_obj, nullptr,
130 nullptr, nullptr, lb.data(), ub.data()));
131}
132
133absl::Status Xpress::AddConstrs(const absl::Span<const char> sense,
134 const absl::Span<const double> rhs,
135 const absl::Span<const double> rng) {
136 const int num_cons = static_cast<int>(sense.size());
137 if (rhs.size() != num_cons) {
138 return absl::InvalidArgumentError(
139 "RHS must have one element per constraint.");
140 }
141 return ToStatus(XPRSaddrows(xpress_model_, num_cons, 0, sense.data(),
142 rhs.data(), rng.data(), nullptr, nullptr,
143 nullptr));
144}
145
146absl::Status Xpress::AddConstrs(const absl::Span<const char> rowtype,
147 const absl::Span<const double> rhs,
148 const absl::Span<const double> rng,
149 const absl::Span<const int> start,
150 const absl::Span<const int> colind,
151 const absl::Span<const double> rowcoef) {
152 const int num_cons = static_cast<int>(rowtype.size());
153 if (rhs.size() != num_cons) {
154 return absl::InvalidArgumentError(
155 "RHS must have one element per constraint.");
156 }
157 if (start.size() != num_cons) {
158 return absl::InvalidArgumentError(
159 "START must have one element per constraint.");
160 }
161 if (colind.size() != rowcoef.size()) {
162 return absl::InvalidArgumentError(
163 "COLIND and ROWCOEF must be of the same size.");
164 }
165 return ToStatus(XPRSaddrows(xpress_model_, num_cons, 0, rowtype.data(),
166 rhs.data(), rng.data(), start.data(),
167 colind.data(), rowcoef.data()));
168}
169
170absl::Status Xpress::SetObjectiveSense(bool maximize) {
171 return ToStatus(XPRSchgobjsense(
172 xpress_model_, maximize ? XPRS_OBJ_MAXIMIZE : XPRS_OBJ_MINIMIZE));
173}
174
176 double constant, const absl::Span<const int> col_index,
177 const absl::Span<const double> obj_coeffs) {
178 static int indexes[1] = {-1};
179 double xprs_values[1] = {-constant};
180 RETURN_IF_ERROR(ToStatus(XPRSchgobj(xpress_model_, 1, indexes, xprs_values)))
181 << "Failed to set objective offset in XPRESS";
182
183 const int n_cols = static_cast<int>(col_index.size());
184 return ToStatus(
185 XPRSchgobj(xpress_model_, n_cols, col_index.data(), obj_coeffs.data()));
186}
187
189 const absl::Span<const int> colind1, const absl::Span<const int> colind2,
190 const absl::Span<const double> coefficients) {
191 const int ncoefs = static_cast<int>(coefficients.size());
192 return ToStatus(XPRSchgmqobj(xpress_model_, ncoefs, colind1.data(),
193 colind2.data(), coefficients.data()));
194}
195
196absl::Status Xpress::ChgCoeffs(absl::Span<const int> rowind,
197 absl::Span<const int> colind,
198 absl::Span<const double> values) {
199 const XPRSint64 n_coefs = static_cast<XPRSint64>(rowind.size());
200 return ToStatus(XPRSchgmcoef64(xpress_model_, n_coefs, rowind.data(),
201 colind.data(), values.data()));
202}
203
204absl::Status Xpress::LpOptimize(std::string flags) {
205 return ToStatus(XPRSlpoptimize(xpress_model_, flags.c_str()));
206}
207
208absl::Status Xpress::GetLpSol(absl::Span<double> primals,
209 absl::Span<double> duals,
210 absl::Span<double> reducedCosts) {
211 return ToStatus(XPRSgetlpsol(xpress_model_, primals.data(), nullptr,
212 duals.data(), reducedCosts.data()));
213}
214
215absl::Status Xpress::PostSolve() {
216 return ToStatus(XPRSpostsolve(xpress_model_));
217}
218
219absl::Status Xpress::MipOptimize() {
220 return ToStatus(XPRSmipoptimize(xpress_model_, nullptr));
221}
222
224
225absl::StatusOr<int> Xpress::GetIntControl(int control) const {
226 int result;
227 RETURN_IF_ERROR(ToStatus(XPRSgetintcontrol(xpress_model_, control, &result)))
228 << "Error getting Xpress int control: " << control;
229 return result;
230}
231
232absl::Status Xpress::SetIntControl(int control, int value) {
233 return ToStatus(XPRSsetintcontrol(xpress_model_, control, value));
234}
235
236absl::Status Xpress::ResetIntControl(int control) {
237 if (int_control_defaults_.count(control)) {
238 return ToStatus(XPRSsetintcontrol(xpress_model_, control,
239 int_control_defaults_[control]));
240 }
241 return absl::InvalidArgumentError(
242 "Default value unknown for control " + std::to_string(control) +
243 ", consider adding it to Xpress::initIntControlDefaults");
244}
245
246absl::StatusOr<int> Xpress::GetIntAttr(int attribute) const {
247 int result;
248 RETURN_IF_ERROR(ToStatus(XPRSgetintattrib(xpress_model_, attribute, &result)))
249 << "Error getting Xpress int attribute: " << attribute;
250 return result;
251}
252
253absl::StatusOr<double> Xpress::GetDoubleAttr(int attribute) const {
254 double result;
255 RETURN_IF_ERROR(ToStatus(XPRSgetdblattrib(xpress_model_, attribute, &result)))
256 << "Error getting Xpress double attribute: " << attribute;
257 return result;
258}
259
261 int n;
262 XPRSgetintattrib(xpress_model_, XPRS_ROWS, &n);
263 return n;
264}
265
267 int n;
268 XPRSgetintattrib(xpress_model_, XPRS_COLS, &n);
269 return n;
270}
271
272absl::StatusOr<int> Xpress::GetDualStatus() const {
273 int status = 0;
274 double values[1];
275 // Even though we do not need the values, we have to fetch them, otherwise
276 // we'd get a segmentation fault
277 RETURN_IF_ERROR(ToStatus(XPRSgetduals(xpress_model_, &status, values, 0, 0)))
278 << "Failed to retrieve dual status from XPRESS";
279 return status;
280}
281
282absl::Status Xpress::GetBasis(std::vector<int>& rowBasis,
283 std::vector<int>& colBasis) const {
284 rowBasis.resize(GetNumberOfConstraints());
285 colBasis.resize(GetNumberOfVariables());
286 return ToStatus(
287 XPRSgetbasis(xpress_model_, rowBasis.data(), colBasis.data()));
288}
289
290absl::Status Xpress::SetStartingBasis(std::vector<int>& rowBasis,
291 std::vector<int>& colBasis) const {
292 if (rowBasis.size() != colBasis.size()) {
293 return absl::InvalidArgumentError(
294 "Row basis and column basis must be of same size.");
295 }
296 return ToStatus(
297 XPRSloadbasis(xpress_model_, rowBasis.data(), colBasis.data()));
298}
299
300absl::StatusOr<std::vector<double>> Xpress::GetVarLb() const {
301 int nVars = GetNumberOfVariables();
302 std::vector<double> bounds;
303 bounds.reserve(nVars);
305 ToStatus(XPRSgetlb(xpress_model_, bounds.data(), 0, nVars - 1)))
306 << "Failed to retrieve variable LB from XPRESS";
307 return bounds;
308}
309absl::StatusOr<std::vector<double>> Xpress::GetVarUb() const {
310 int nVars = GetNumberOfVariables();
311 std::vector<double> bounds;
312 bounds.reserve(nVars);
314 ToStatus(XPRSgetub(xpress_model_, bounds.data(), 0, nVars - 1)))
315 << "Failed to retrieve variable UB from XPRESS";
316 return bounds;
317}
318
319} // namespace operations_research::math_opt
#define RETURN_IF_ERROR(expr)
absl::Status ChgCoeffs(absl::Span< const int > rowind, absl::Span< const int > colind, absl::Span< const double > values)
Definition g_xpress.cc:196
absl::StatusOr< std::vector< double > > GetVarUb() const
Definition g_xpress.cc:309
absl::Status SetIntControl(int control, int value)
Definition g_xpress.cc:232
absl::Status SetStartingBasis(std::vector< int > &rowBasis, std::vector< int > &colBasis) const
Definition g_xpress.cc:290
absl::StatusOr< int > GetDualStatus() const
Definition g_xpress.cc:272
absl::StatusOr< int > GetIntAttr(int attribute) const
Definition g_xpress.cc:246
absl::Status SetProbName(absl::string_view name)
Definition g_xpress.cc:71
absl::Status GetLpSol(absl::Span< double > primals, absl::Span< double > duals, absl::Span< double > reducedCosts)
Definition g_xpress.cc:208
absl::Status AddConstrs(absl::Span< const char > sense, absl::Span< const double > rhs, absl::Span< const double > rng)
Definition g_xpress.cc:133
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:99
absl::Status SetLinearObjective(double constant, absl::Span< const int > col_index, absl::Span< const double > obj_coeffs)
Definition g_xpress.cc:175
static absl::StatusOr< std::unique_ptr< Xpress > > New(absl::string_view model_name)
Creates a new Xpress.
Definition g_xpress.cc:62
absl::Status ResetIntControl(int control)
Definition g_xpress.cc:236
absl::Status LpOptimize(std::string flags)
Definition g_xpress.cc:204
absl::Status SetQuadraticObjective(absl::Span< const int > colind1, absl::Span< const int > colind2, absl::Span< const double > coefficients)
Definition g_xpress.cc:188
absl::StatusOr< int > GetIntControl(int control) const
Definition g_xpress.cc:225
absl::StatusOr< double > GetDoubleAttr(int attribute) const
Definition g_xpress.cc:253
static void XPRS_CC printXpressMessage(XPRSprob prob, void *data, const char *sMsg, int nLen, int nMsgLvl)
Definition g_xpress.cc:80
absl::Status GetBasis(std::vector< int > &rowBasis, std::vector< int > &colBasis) const
Definition g_xpress.cc:282
absl::Status SetObjectiveSense(bool maximize)
Definition g_xpress.cc:170
absl::StatusOr< std::vector< double > > GetVarLb() const
Definition g_xpress.cc:300
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
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
#define XPRSint64
Definition environment.h:56
struct xo_prob_struct * XPRSprob
Copyright 2019-2023 RTE.
Definition environment.h:27
#define XPRS_MAXPROBNAMELENGTH
#define XPRS_LPITERLIMIT
#define XPRS_BARITERLIMIT
#define XPRS_STOP_USER
Definition environment.h:77
#define XPRS_CC
NOLINTEND(runtime/int)
Definition environment.h:63
#define XPRS_OBJ_MINIMIZE