24#include "absl/log/check.h" 
   25#include "absl/memory/memory.h" 
   26#include "absl/status/status.h" 
   27#include "absl/status/statusor.h" 
   28#include "absl/strings/str_cat.h" 
   29#include "absl/strings/str_join.h" 
   30#include "absl/time/clock.h" 
   31#include "absl/time/time.h" 
   32#include "absl/types/span.h" 
   51  std::vector<std::string> warnings;
 
   52  if (parameters.has_threads() && parameters.threads() > 1) {
 
   53    warnings.push_back(absl::StrCat(
 
   54        "XpressSolver only supports parameters.threads = 1; value ",
 
   55        parameters.threads(), 
" is not supported"));
 
   61    warnings.emplace_back(absl::StrCat(
 
   62        "XpressSolver does not support the 'lp_algorithm' parameter value: ",
 
   65  if (parameters.has_objective_limit()) {
 
   66    warnings.emplace_back(
"XpressSolver does not support objective_limit yet");
 
   68  if (parameters.has_best_bound_limit()) {
 
   69    warnings.emplace_back(
"XpressSolver does not support best_bound_limit yet");
 
   71  if (parameters.has_cutoff_limit()) {
 
   72    warnings.emplace_back(
"XpressSolver does not support cutoff_limit yet");
 
   74  if (!warnings.empty()) {
 
   75    return absl::InvalidArgumentError(absl::StrJoin(warnings, 
"; "));
 
   77  return absl::OkStatus();
 
   94    return absl::InvalidArgumentError(
"Xpress is not correctly installed.");
 
  103  auto xpress_solver = absl::WrapUnique(
new XpressSolver(std::move(xpr)));
 
  105  return xpress_solver;
 
 
  108absl::Status XpressSolver::LoadModel(
const ModelProto& input_model) {
 
  109  CHECK(xpress_ != 
nullptr);
 
  115  return absl::OkStatus();
 
  117absl::Status XpressSolver::AddNewVariables(
 
  118    const VariablesProto& new_variables) {
 
  119  const int num_new_variables = new_variables.lower_bounds().size();
 
  120  std::vector<char> variable_type(num_new_variables);
 
  121  int n_variables = xpress_->GetNumberOfVariables();
 
  122  for (
int j = 0; j < num_new_variables; ++j) {
 
  123    const VarId 
id = new_variables.ids(j);
 
  127    if (new_variables.integers(j)) {
 
  129      return absl::UnimplementedError(
"XpressSolver does not handle MIPs yet");
 
  133                                   new_variables.upper_bounds(),
 
  139  return absl::OkStatus();
 
  142XpressSolver::XpressSolver(std::unique_ptr<Xpress> g_xpress)
 
  143    : xpress_(std::move(g_xpress)) {}
 
  145absl::Status XpressSolver::AddNewLinearConstraints(
 
  148  const int num_new_constraints = constraints.lower_bounds().size();
 
  149  std::vector<char> constraint_sense;
 
  150  constraint_sense.reserve(num_new_constraints);
 
  151  std::vector<double> constraint_rhs;
 
  152  constraint_rhs.reserve(num_new_constraints);
 
  153  std::vector<double> constraint_rng;
 
  154  constraint_rng.reserve(num_new_constraints);
 
  155  int n_constraints = xpress_->GetNumberOfConstraints();
 
  156  for (
int i = 0; 
i < num_new_constraints; ++
i) {
 
  157    const int64_t 
id = constraints.ids(i);
 
  158    LinearConstraintData& constraint_data =
 
  160    const double lb = constraints.lower_bounds(i);
 
  161    const double ub = constraints.upper_bounds(i);
 
  162    constraint_data.lower_bound = lb;
 
  163    constraint_data.upper_bound = ub;
 
  164    constraint_data.constraint_index = 
i + n_constraints;
 
  168    const bool lb_is_xprs_neg_inf = lb <= kMinusInf;
 
  169    const bool ub_is_xprs_pos_inf = ub >= kPlusInf;
 
  170    if (lb_is_xprs_neg_inf && !ub_is_xprs_pos_inf) {
 
  173    } 
else if (!lb_is_xprs_neg_inf && ub_is_xprs_pos_inf) {
 
  176    } 
else if (lb == ub) {
 
  184    constraint_sense.emplace_back(sense);
 
  185    constraint_rhs.emplace_back(rhs);
 
  186    constraint_rng.emplace_back(rng);
 
  189  return xpress_->AddConstrs(constraint_sense, constraint_rhs, constraint_rng);
 
  192absl::Status XpressSolver::AddSingleObjective(
const ObjectiveProto& objective) {
 
  195  is_maximize_ = objective.maximize();
 
  197  std::vector<int> index;
 
  198  index.reserve(objective.linear_coefficients().ids_size());
 
  199  for (
const int64_t 
id : objective.linear_coefficients().ids()) {
 
  200    index.push_back(variables_map_.at(
id));
 
  203      objective.offset(), index, objective.linear_coefficients().values()));
 
  205  const int num_terms = objective.quadratic_coefficients().row_ids().size();
 
  207    std::vector<int> first_var_index(num_terms);
 
  208    std::vector<int> second_var_index(num_terms);
 
  209    std::vector<double> coefficients(num_terms);
 
  210    for (
int k = 0; k < num_terms; ++k) {
 
  211      const int64_t row_id = objective.quadratic_coefficients().row_ids(k);
 
  212      const int64_t column_id =
 
  213          objective.quadratic_coefficients().column_ids(k);
 
  214      first_var_index[k] = variables_map_.at(row_id);
 
  215      second_var_index[k] = variables_map_.at(column_id);
 
  218      double m = first_var_index[k] == second_var_index[k] ? 2 : 1;
 
  219      coefficients[k] = objective.quadratic_coefficients().coefficients(k) * m;
 
  222        first_var_index, second_var_index, coefficients));
 
  224  return absl::OkStatus();
 
  227absl::Status XpressSolver::ChangeCoefficients(
 
  229  const int num_coefficients = matrix.row_ids().size();
 
  230  std::vector<int> row_index;
 
  231  row_index.reserve(num_coefficients);
 
  232  std::vector<int> col_index;
 
  233  col_index.reserve(num_coefficients);
 
  234  for (
int k = 0; k < num_coefficients; ++k) {
 
  236        linear_constraints_map_.at(matrix.row_ids(k)).constraint_index);
 
  237    col_index.push_back(variables_map_.at(matrix.column_ids(k)));
 
  239  return xpress_->ChgCoeffs(row_index, col_index, matrix.coefficients());
 
  249  const absl::Time start = absl::Now();
 
  260                     ListInvertedBounds());
 
  269  RETURN_IF_ERROR(CallXpressSolve(parameters)) << 
"Error during XPRESS solve";
 
  273      ExtractSolveResultProto(start, model_parameters, parameters));
 
 
  278std::string XpressSolver::GetLpOptimizationFlags(
 
  294      switch (default_alg.value_or(-1)) {
 
  310absl::Status XpressSolver::CallXpressSolve(
 
  313  if (parameters.enable_output()) {
 
  315        << 
"Unable to enable XPRESS logs";
 
  323        << 
"Could not set iteration limits.";
 
  324    RETURN_IF_ERROR(xpress_->LpOptimize(GetLpOptimizationFlags(parameters)));
 
  327    xpress_lp_status_ = {primal_status, dual_status};
 
  332    RETURN_IF_ERROR(xpress_->PostSolve()) << 
"Post-solve failed in XPRESS";
 
  335  if (parameters.enable_output()) {
 
  337        << 
"Unable to disable XPRESS logs";
 
  339  return absl::OkStatus();
 
  342absl::Status XpressSolver::SetLpIterLimits(
 
  347  if (parameters.has_iteration_limit()) {
 
  350        << 
"Could not set XPRS_LPITERLIMIT";
 
  353        << 
"Could not set XPRS_BARITERLIMIT";
 
  356        << 
"Could not reset XPRS_LPITERLIMIT to its default value";
 
  358        << 
"Could not reset XPRS_BARITERLIMIT to its default value";
 
  360  return absl::OkStatus();
 
  363absl::StatusOr<SolveResultProto> XpressSolver::ExtractSolveResultProto(
 
  366  SolveResultProto result;
 
  368                   GetSolution(model_parameters, solve_parameters));
 
  369  *result.add_solutions() = std::move(
solution);
 
  374      *result.mutable_termination(),
 
  375      ConvertTerminationReason(best_primal_bound, best_dual_bound));
 
  379absl::StatusOr<double> XpressSolver::GetBestPrimalBound()
 const {
 
  381      (isPrimalFeasible() ||
 
  387  return is_maximize_ ? kMinusInf : kPlusInf;
 
  390absl::StatusOr<double> XpressSolver::GetBestDualBound()
 const {
 
  392      (isPrimalFeasible() ||
 
  398  return is_maximize_ ? kPlusInf : kMinusInf;
 
  401absl::StatusOr<SolutionProto> XpressSolver::GetSolution(
 
  405    return absl::UnimplementedError(
"XpressSolver does not handle MIPs yet");
 
  407    return GetLpSolution(model_parameters, solve_parameters);
 
  411absl::StatusOr<SolutionProto> XpressSolver::GetLpSolution(
 
  415  int nVars = xpress_->GetNumberOfVariables();
 
  416  int nCons = xpress_->GetNumberOfConstraints();
 
  417  std::vector<double> primals(nVars);
 
  418  std::vector<double> duals(nCons);
 
  419  std::vector<double> reducedCosts(nVars);
 
  423          ->GetLpSol(absl::MakeSpan(primals), absl::MakeSpan(duals),
 
  424                     absl::MakeSpan(reducedCosts))
 
  429  if (isPrimalFeasible()) {
 
  431    solution.mutable_primal_solution()->set_feasibility_status(
 
  432        getLpSolutionStatus());
 
  434    solution.mutable_primal_solution()->set_objective_value(primalBound);
 
  435    XpressVectorToSparseDoubleVector(
 
  436        primals, variables_map_,
 
  437        *
solution.mutable_primal_solution()->mutable_variable_values(),
 
  438        model_parameters.variable_values_filter());
 
  443    solution.mutable_dual_solution()->set_feasibility_status(
 
  444        getDualSolutionStatus());
 
  446    solution.mutable_dual_solution()->set_objective_value(dualBound);
 
  447    XpressVectorToSparseDoubleVector(
 
  448        duals, linear_constraints_map_,
 
  449        *
solution.mutable_dual_solution()->mutable_dual_values(),
 
  450        model_parameters.dual_values_filter());
 
  451    XpressVectorToSparseDoubleVector(
 
  452        reducedCosts, variables_map_,
 
  453        *
solution.mutable_dual_solution()->mutable_reduced_costs(),
 
  454        model_parameters.reduced_costs_filter());
 
  459  if (basis.has_value()) {
 
  460    *
solution.mutable_basis() = std::move(*basis);
 
  465bool XpressSolver::isPrimalFeasible()
 const {
 
  475bool XpressSolver::isDualFeasible()
 const {
 
  477    return isPrimalFeasible();
 
  488  switch (xpress_lp_status_.primal_status) {
 
  510  if (isDualFeasible()) {
 
  513  switch (xpress_lp_status_.dual_status) {
 
  570absl::Status XpressSolver::SetXpressStartingBasis(
const BasisProto& basis) {
 
  571  std::vector<int> xpress_var_basis_status(xpress_->GetNumberOfVariables());
 
  572  for (
const auto [
id, value] : 
MakeView(basis.variable_status())) {
 
  573    xpress_var_basis_status[variables_map_.
at(
id)] =
 
  576  std::vector<int> xpress_constr_basis_status(
 
  577      xpress_->GetNumberOfConstraints());
 
  578  for (
const auto [
id, value] : 
MakeView(basis.constraint_status())) {
 
  579    xpress_constr_basis_status[linear_constraints_map_.
at(
id)
 
  583  return xpress_->SetStartingBasis(xpress_constr_basis_status,
 
  584                                   xpress_var_basis_status);
 
  587absl::StatusOr<std::optional<BasisProto>> XpressSolver::GetBasisIfAvailable(
 
  589  std::vector<int> xprs_variable_basis_status;
 
  590  std::vector<int> xprs_constraint_basis_status;
 
  592           ->GetBasis(xprs_constraint_basis_status, xprs_variable_basis_status)
 
  599  for (
auto [variable_id, xprs_variable_index] : variables_map_) {
 
  600    basis.mutable_variable_status()->add_ids(variable_id);
 
  602        xprs_variable_basis_status[xprs_variable_index], 
false);
 
  604      return absl::InternalError(
 
  605          absl::StrCat(
"Invalid Xpress variable basis status: ",
 
  606                       xprs_variable_basis_status[xprs_variable_index]));
 
  608    basis.mutable_variable_status()->add_values(variable_status);
 
  612  for (
auto [constraint_id, xprs_ct_index] : linear_constraints_map_) {
 
  613    basis.mutable_constraint_status()->add_ids(constraint_id);
 
  615        xprs_constraint_basis_status[xprs_ct_index.constraint_index], 
true);
 
  617      return absl::InternalError(absl::StrCat(
 
  618          "Invalid Xpress constraint basis status: ",
 
  619          xprs_constraint_basis_status[xprs_ct_index.constraint_index]));
 
  621    basis.mutable_constraint_status()->add_values(status);
 
  625  basis.set_basic_dual_feasibility(
 
  630absl::StatusOr<SolveStatsProto> XpressSolver::GetSolveStats(
 
  631    absl::Time start)
 const {
 
  632  SolveStatsProto solve_stats;
 
  634                                           solve_stats.mutable_solve_time()));
 
  639    solve_stats.set_simplex_iterations(iters);
 
  644    solve_stats.set_barrier_iterations(iters);
 
  652void XpressSolver::XpressVectorToSparseDoubleVector(
 
  653    absl::Span<const double> xpress_values, 
const T& map,
 
  656  SparseVectorFilterPredicate predicate(filter);
 
  657  for (
auto [
id, xpress_data] : map) {
 
  658    const double value = xpress_values[get_model_index(xpress_data)];
 
  659    if (predicate.AcceptsAndUpdate(
id, value)) {
 
  661      result.add_values(value);
 
  666absl::StatusOr<TerminationProto> XpressSolver::ConvertTerminationReason(
 
  667    double best_primal_bound, 
double best_dual_bound)
 const {
 
  669    switch (xpress_lp_status_.primal_status) {
 
  673            "Problem solve has not started (XPRS_LP_UNSTARTED)");
 
  682            is_maximize_, 
"Objective worse than cutoff (XPRS_LP_CUTOFF)");
 
  688            "Solve did not finish (XPRS_LP_UNFINISHED)");
 
  691                                         "Xpress status XPRS_LP_UNBOUNDED");
 
  694            is_maximize_, 
"Cutoff in dual (XPRS_LP_CUTOFF_IN_DUAL)");
 
  698            "Problem could not be solved due to numerical issues " 
  699            "(XPRS_LP_UNSOLVED)");
 
  702                                  "Problem contains quadratic data, which is " 
  703                                  "not convex (XPRS_LP_NONCONVEX)");
 
  705        return absl::InternalError(
 
  706            absl::StrCat(
"Missing Xpress LP status code case: ",
 
  707                         xpress_lp_status_.primal_status));
 
  710    return absl::UnimplementedError(
"XpressSolver does not handle MIPs yet");
 
  719absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
 
  723  return absl::UnimplementedError(
 
  724      "XPRESS does not provide a method to compute an infeasible subsystem");
 
 
  727absl::StatusOr<InvertedBounds> XpressSolver::ListInvertedBounds()
 const {
 
  732    for (
const auto& [
id, index] : variables_map_) {
 
  733      if (var_lbs[index] > var_ubs[index]) {
 
  741  for (
const auto& [
id, cstr_data] : linear_constraints_map_) {
 
  742    if (cstr_data.lower_bound > cstr_data.upper_bound) {
 
  750  return inverted_bounds;
 
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
mapped_type & at(const key_arg< K > &key)
const ::operations_research::math_opt::VariablesProto & variables() const
const ::operations_research::math_opt::LinearConstraintsProto & linear_constraints() const
const ::std::string & name() const
const ::operations_research::math_opt::ObjectiveProto & objective() const
const ::operations_research::math_opt::SparseDoubleMatrixProto & linear_constraint_matrix() const
const ::operations_research::math_opt::BasisProto & initial_basis() const
bool has_initial_basis() const
.operations_research.math_opt.BasisProto initial_basis = 4;
::operations_research::math_opt::LPAlgorithmProto lp_algorithm() const
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >( const CallbackDataProto &)> Callback
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto ¶meters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, const SolveInterrupter *interrupter) override
Solves the optimization problem.
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update) override
Updates the problem (not implemented yet)
absl::StatusOr< ComputeInfeasibleSubsystemResultProto > ComputeInfeasibleSubsystem(const SolveParametersProto ¶meters, MessageCallback message_cb, const SolveInterrupter *interrupter) override
Computes the infeasible subsystem (not implemented yet)
static absl::StatusOr< std::unique_ptr< XpressSolver > > New(const ModelProto &input_model, const SolverInterface::InitArgs &init_args)
Creates the XPRESS solver and loads the model into it.
static absl::StatusOr< std::unique_ptr< Xpress > > New(absl::string_view model_name)
Creates a new Xpress.
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
auto & InsertKeyOrDie(Collection *const collection, const typename Collection::value_type::first_type &key)
An object oriented wrapper for quadratic constraints in ModelStorage.
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
absl::Status ModelIsSupported(const ModelProto &model, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
@ TERMINATION_REASON_OTHER_ERROR
@ TERMINATION_REASON_NUMERICAL_ERROR
constexpr SupportedProblemStructures kXpressSupportedStructures
absl::Status ModelSolveParametersAreSupported(const ModelSolveParametersProto &model_parameters, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
@ SOLUTION_STATUS_UNDETERMINED
@ SOLUTION_STATUS_INFEASIBLE
@ SOLUTION_STATUS_UNSPECIFIED
@ SOLUTION_STATUS_FEASIBLE
@ LP_ALGORITHM_DUAL_SIMPLEX
@ LP_ALGORITHM_UNSPECIFIED
@ LP_ALGORITHM_PRIMAL_SIMPLEX
BasisStatusProto XpressToMathOptBasisStatus(const int status, bool isConstraint)
TerminationProto OptimalTerminationProto(const double finite_primal_objective, const double dual_objective, const absl::string_view detail)
@ FEASIBILITY_STATUS_FEASIBLE
@ FEASIBILITY_STATUS_UNDETERMINED
TerminationProto FeasibleTerminationProto(const bool is_maximize, const LimitProto limit, const double primal_objective, const std::optional< double > optional_dual_objective, const absl::string_view detail)
absl::Status CheckRegisteredCallbackEvents(const CallbackRegistrationProto ®istration, const absl::flat_hash_set< CallbackEventProto > &supported_events)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
TerminationProto CutoffTerminationProto(bool is_maximize, const absl::string_view detail)
Calls NoSolutionFoundTerminationProto() with LIMIT_CUTOFF LIMIT.
@ BASIS_STATUS_AT_UPPER_BOUND
@ BASIS_STATUS_UNSPECIFIED
@ BASIS_STATUS_AT_LOWER_BOUND
TerminationProto InfeasibleTerminationProto(bool is_maximize, const FeasibilityStatusProto dual_feasibility_status, const absl::string_view detail)
TerminationProto UnboundedTerminationProto(const bool is_maximize, const absl::string_view detail)
int MathOptToXpressBasisStatus(const BasisStatusProto status, bool isConstraint)
In SWIG mode, we don't want anything besides these top-level includes.
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
std::string ProtoEnumToString(ProtoEnumType enum_value)
bool XpressIsCorrectlyInstalled()
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
#define MATH_OPT_REGISTER_SOLVER(solver_type, solver_factory)
std::vector< int64_t > linear_constraints
Ids of the linear constraints with inverted bounds.
std::vector< int64_t > variables
Ids of the variables with inverted bounds.
absl::Status ToStatus() const
Initialization arguments.
#define XPRS_LP_CUTOFF_IN_DUAL
#define XPRS_MIP_SOLUTION
#define XPRS_LP_NONCONVEX
#define XPRS_GREATER_EQUAL
#define XPRS_SOLSTATUS_FEASIBLE
#define XPRS_LP_UNFINISHED
#define XPRS_LP_UNSTARTED
#define XPRS_SOLSTATUS_UNBOUNDED
#define XPRS_SOLSTATUS_INFEASIBLE
#define XPRS_BARITERLIMIT
#define XPRS_LP_UNBOUNDED
#define XPRS_SOLSTATUS_OPTIMAL
#define XPRS_SOLSTATUS_NOTFOUND
#define XPRS_INTEGER
Initial version of this code was provided by RTE.