linear_solver.h 66.2 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
// Copyright 2010-2018 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * \file
 * A C++ wrapper that provides a simple and unified interface to
 * several linear programming and mixed integer programming solvers:
 * GLOP, GLPK, CLP, CBC, and SCIP. The wrapper can also be used in Java, C#,
 * and Python via SWIG.
 *
 * What is Linear Programming?
 *
 *   In mathematics, linear programming (LP) is a technique for optimization of
 *   a linear objective function, subject to linear equality and linear
 *   inequality constraints. Informally, linear programming determines the way
 *   to achieve the best outcome (such as maximum profit or lowest cost) in a
 *   given mathematical model and given some list of requirements represented
 *   as linear equations.
 *
 *   The most widely used technique for solving a linear program is the Simplex
 *   algorithm, devised by George Dantzig in 1947. It performs very well on
 *   most instances, for which its running time is polynomial. A lot of effort
 *   has been put into improving the algorithm and its implementation. As a
 *   byproduct, it has however been shown that one can always construct
 *   problems that take exponential time for the Simplex algorithm to solve.
 *   Research has thus focused on trying to find a polynomial algorithm for
 *   linear programming, or to prove that linear programming is indeed
 *   polynomial.
 *
 *   Leonid Khachiyan first exhibited in 1979 a weakly polynomial algorithm for
 *   linear programming. "Weakly polynomial" means that the running time of the
 *   algorithm is in O(P(n) * 2^p) where P(n) is a polynomial of the size of the
 *   problem, and p is the precision of computations expressed in number of
 *   bits. With a fixed-precision, floating-point-based implementation, a weakly
 *   polynomial algorithm will  thus run in polynomial time. No implementation
 *   of Khachiyan's algorithm has proved efficient, but a larger breakthrough in
 *   the field came in 1984 when Narendra Karmarkar introduced a new interior
 *   point method for solving linear programming problems. Interior point
 *   algorithms have proved efficient on very large linear programs.
 *
 *   Check Wikipedia for more detail:
 *     http://en.wikipedia.org/wiki/Linear_programming
 *
 * -----------------------------------
 *
 * Example of a Linear Program
 *
 *   maximize:
 *     3x + y
 *   subject to:
 *     1.5 x + 2 y <= 12
 *     0 <= x <= 3
 *     0 <= y <= 5
 *
 *  A linear program has:
 *    1) a linear objective function
 *    2) linear constraints that can be equalities or inequalities
 *    3) bounds on variables that can be positive, negative, finite or
 *       infinite.
 *
 * -----------------------------------
 *
 * What is Mixed Integer Programming?
 *
 *   Here, the constraints and the objective are still linear but
 *   there are additional integrality requirements for variables. If
 *   all variables are required to take integer values, then the
 *   problem is called an integer program (IP). In most cases, only
 *   some variables are required to be integer and the rest of the
 *   variables are continuous: this is called a mixed integer program
 *   (MIP). IPs and MIPs are generally NP-hard.
 *
 *   Integer variables can be used to model discrete decisions (build a
 *   datacenter in city A or city B), logical relationships (only
 *   place machines in datacenter A if we have decided to build
 *   datacenter A) and approximate non-linear functions with piecewise
 *   linear functions (for example, the cost of machines as a function
 *   of how many machines are bought, or the latency of a server as a
 *   function of its load).
 *
 * -----------------------------------
 *
 * How to use the wrapper
 *
 *   The user builds the model and solves it through the MPSolver class,
 *   then queries the solution through the MPSolver, MPVariable and
 *   MPConstraint classes. To be able to query a solution, you need the
 *   following:
 *   - A solution exists: MPSolver::Solve has been called and a solution
 *     has been found.
 *   - The model has not been modified since the last time
 *     MPSolver::Solve was called. Otherwise, the solution obtained
 *     before the model modification may not longer be feasible or
 *     optimal.
 *
 * @see ../examples/linear_programming.cc for a simple LP example.
 *
 * @see ../examples/integer_programming.cc for a simple MIP example.
 *
 *   All methods cannot be called successfully in all cases. For
 *   example: you cannot query a solution when no solution exists, you
 *   cannot query a reduced cost value (which makes sense only on
 *   continuous problems) on a discrete problem. When a method is
 *   called in an unsuitable context, it aborts with a
 *   LOG(FATAL).
 * TODO(user): handle failures gracefully.
 *
 * -----------------------------------
 *
 * For developers: How the wrapper works
 *
 *   MPSolver stores a representation of the model (variables,
 *   constraints and objective) in its own data structures and a
 *   pointer to a MPSolverInterface that wraps the underlying solver
 *   (GLOP, CBC, CLP, GLPK, or SCIP) that does the actual work. The
 *   underlying solver also keeps a representation of the model in its
 *   own data structures. The model representations in MPSolver and in
 *   the underlying solver are kept in sync by the 'extraction'
 *   mechanism: synchronously for some changes and asynchronously
 *   (when MPSolver::Solve is called) for others. Synchronicity
 *   depends on the modification applied and on the underlying solver.
 */

#ifndef OR_TOOLS_LINEAR_SOLVER_LINEAR_SOLVER_H_
#define OR_TOOLS_LINEAR_SOLVER_LINEAR_SOLVER_H_

#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/strings/match.h"
#include "absl/strings/str_format.h"
#include "absl/types/optional.h"
#include "ortools/base/commandlineflags.h"
#include "ortools/base/integral_types.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/base/timer.h"
#include "ortools/linear_solver/linear_expr.h"
#include "ortools/linear_solver/linear_solver.pb.h"
#include "ortools/linear_solver/linear_solver_callback.h"
#include "ortools/port/proto_utils.h"

namespace operations_research {

constexpr double kDefaultPrimalTolerance = 1e-07;

class MPConstraint;
class MPObjective;
class MPSolverInterface;
class MPSolverParameters;
class MPVariable;

// There is a homonymous version taking a MPSolver::OptimizationProblemType.
bool SolverTypeIsMip(MPModelRequest::SolverType solver_type);

/**
 * This mathematical programming (MP) solver class is the main class
 * though which users build and solve problems.
 */
class MPSolver {
 public:
  /**
   * The type of problems (LP or MIP) that will be solved and the underlying
   *  solver (GLOP, GLPK, CLP, CBC or SCIP) that will solve them. This must
   * remain consistent with MPModelRequest::OptimizationProblemType
   *  (take particular care of the open-source version).
   */
  enum OptimizationProblemType {
186 187
    // Linear programming problems.
    // ----------------------------
Valentin Platzgummer's avatar
Valentin Platzgummer committed
188 189
    CLP_LINEAR_PROGRAMMING = 0,
    GLPK_LINEAR_PROGRAMMING = 1,
190
    GLOP_LINEAR_PROGRAMMING = 2,  // Recommended default value. Made in Google.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
191

192 193
    // Integer programming problems.
    // -----------------------------
Valentin Platzgummer's avatar
Valentin Platzgummer committed
194 195 196
    SCIP_MIXED_INTEGER_PROGRAMMING = 3,  // Recommended default value.
    GLPK_MIXED_INTEGER_PROGRAMMING = 4,
    CBC_MIXED_INTEGER_PROGRAMMING = 5,
197 198 199

    // Commercial software (need license).
    GUROBI_LINEAR_PROGRAMMING = 6,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
200
    GUROBI_MIXED_INTEGER_PROGRAMMING = 7,
201
    CPLEX_LINEAR_PROGRAMMING = 10,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
202 203 204
    CPLEX_MIXED_INTEGER_PROGRAMMING = 11,
    XPRESS_LINEAR_PROGRAMMING = 101,
    XPRESS_MIXED_INTEGER_PROGRAMMING = 102,
205 206 207 208 209 210 211 212 213 214 215 216

    // Boolean optimization problem (requires only integer variables and works
    // best with only Boolean variables).
    BOP_INTEGER_PROGRAMMING = 12,

    // SAT based solver (requires only integer and Boolean variables).
    // If you pass it mixed integer problems, it will scale coefficients to
    // integer values, and solver continuous variables as integral variables.
    SAT_INTEGER_PROGRAMMING = 14,

    // Dedicated knapsack solvers.
    KNAPSACK_MIXED_INTEGER_PROGRAMMING = 13,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
217 218 219 220 221 222
  };

  /// Create a solver with the given name and underlying solver backend.
  MPSolver(const std::string& name, OptimizationProblemType problem_type);
  virtual ~MPSolver();

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
  /**
   * Recommended factory method to create a MPSolver instance, especially in
   * non C++ languages.
   *
   * It returns a newly created solver instance if successful, or a nullptr
   * otherwise. This can occur if the relevant interface is not linked in, or if
   * a needed license is not accessible for commercial solvers.
   *
   * Ownership of the solver is passed on to the caller of this method.
   * It will accept both string names of the OptimizationProblemType enum, as
   * well as a short version (i.e. "SCIP_MIXED_INTEGER_PROGRAMMING" or "SCIP").
   *
   * solver_id is case insensitive, and the following names are supported:
   *   - CLP_LINEAR_PROGRAMMING or CLP
   *   - CBC_MIXED_INTEGER_PROGRAMMING or CBC
   *   - GLOP_LINEAR_PROGRAMMING or GLOP
   *   - BOP_INTEGER_PROGRAMMING or BOP
   *   - SAT_INTEGER_PROGRAMMING or SAT or CP_SAT
   *   - SCIP_MIXED_INTEGER_PROGRAMMING or SCIP
   *   - GUROBI_LINEAR_PROGRAMMING or GUROBI_LP
   *   - GUROBI_MIXED_INTEGER_PROGRAMMING or GUROBI or GUROBI_MIP
   *   - CPLEX_LINEAR_PROGRAMMING or CPLEX_LP
   *   - CPLEX_MIXED_INTEGER_PROGRAMMING or CPLEX or CPLEX_MIP
   *   - XPRESS_LINEAR_PROGRAMMING or XPRESS_LP
   *   - XPRESS_MIXED_INTEGER_PROGRAMMING or XPRESS or XPRESS_MIP
   *   - GLPK_LINEAR_PROGRAMMING or GLPK_LP
   *   - GLPK_MIXED_INTEGER_PROGRAMMING or GLPK or GLPK_MIP
   */
  static MPSolver* CreateSolver(const std::string& name,
                                const std::string& solver_id);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
254 255 256 257 258 259 260 261 262 263
  /**
   * Whether the given problem type is supported (this will depend on the
   * targets that you linked).
   */
  static bool SupportsProblemType(OptimizationProblemType problem_type);

  /**
   * Parses the name of the solver. Returns true if the solver type is
   * successfully parsed as one of the OptimizationProblemType.
   */
264
  static bool ParseSolverType(absl::string_view solver_id,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
265 266
                              OptimizationProblemType* type);

267 268 269 270 271 272 273 274 275 276 277 278 279
  /**
   * Parses the name of the solver and returns the correct optimization type or
   * dies.
   */
  static OptimizationProblemType ParseSolverTypeOrDie(
      const std::string& solver_id);

  /**
   * Parses the name of the solver. Returns true if the solver type is
   * recognized and support for the solver is actually linked in.
   */
  static bool ParseAndCheckSupportForProblemType(const std::string& solver_id);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
  bool IsMIP() const;

  /// Returns the name of the model set at construction.
  const std::string& Name() const {
    return name_;  // Set at construction.
  }

  /// Returns the optimization problem type set at construction.
  virtual OptimizationProblemType ProblemType() const {
    return problem_type_;  // Set at construction.
  }

  /**
   * Clears the objective (including the optimization direction), all variables
   * and constraints. All the other properties of the MPSolver (like the time
   * limit) are kept untouched.
   */
  void Clear();

  /// Returns the number of variables.
  int NumVariables() const { return variables_.size(); }

  /**
   * Returns the array of variables handled by the MPSolver. (They are listed in
   * the order in which they were created.)
   */
  const std::vector<MPVariable*>& variables() const { return variables_; }

  /**
   * Looks up a variable by name, and returns nullptr if it does not exist. The
   * first call has a O(n) complexity, as the variable name index is lazily
   * created upon first use. Will crash if variable names are not unique.
   */
  MPVariable* LookupVariableOrNull(const std::string& var_name) const;

  /**
   * Creates a variable with the given bounds, integrality requirement and
   * name. Bounds can be finite or +/- MPSolver::infinity(). The MPSolver owns
   * the variable (i.e. the returned pointer is borrowed). Variable names are
   * optional. If you give an empty name, name() will auto-generate one for you
   * upon request.
   */
  MPVariable* MakeVar(double lb, double ub, bool integer,
                      const std::string& name);

  /// Creates a continuous variable.
  MPVariable* MakeNumVar(double lb, double ub, const std::string& name);

  /// Creates an integer variable.
  MPVariable* MakeIntVar(double lb, double ub, const std::string& name);

  /// Creates a boolean variable.
  MPVariable* MakeBoolVar(const std::string& name);

  /**
   * Creates an array of variables. All variables created have the same bounds
   * and integrality requirement. If nb <= 0, no variables are created, the
   * function crashes in non-opt mode.
   *
   * @param nb the number of variables to create.
   * @param lb the lower bound of created variables
   * @param ub the upper bound of created variables
   * @param integer controls whether the created variables are continuous or
   * integral.
   * @param name_prefix the prefix of the variable names. Variables are named
   * name_prefix0, name_prefix1, ...
   * @param[out] vars the vector of variables to fill with variables.
   */
  void MakeVarArray(int nb, double lb, double ub, bool integer,
                    const std::string& name_prefix,
                    std::vector<MPVariable*>* vars);

  /// Creates an array of continuous variables.
  void MakeNumVarArray(int nb, double lb, double ub, const std::string& name,
                       std::vector<MPVariable*>* vars);

  ///  Creates an array of integer variables.
  void MakeIntVarArray(int nb, double lb, double ub, const std::string& name,
                       std::vector<MPVariable*>* vars);

  /// Creates an array of boolean variables.
  void MakeBoolVarArray(int nb, const std::string& name,
                        std::vector<MPVariable*>* vars);

  /// Returns the number of constraints.
  int NumConstraints() const { return constraints_.size(); }

  /**
   * Returns the array of constraints handled by the MPSolver.
   *
   * They are listed in the order in which they were created.
   */
  const std::vector<MPConstraint*>& constraints() const { return constraints_; }

  /**
   *  Looks up a constraint by name, and returns nullptr if it does not exist.
   *
   * The first call has a O(n) complexity, as the constraint name index is
   * lazily created upon first use. Will crash if constraint names are not
   * unique.
   */
  MPConstraint* LookupConstraintOrNull(
      const std::string& constraint_name) const;

  /**
   * Creates a linear constraint with given bounds.
   *
   * Bounds can be finite or +/- MPSolver::infinity(). The MPSolver class
   * assumes ownership of the constraint.
   *
   * @return a pointer to the newly created constraint.
   */
  MPConstraint* MakeRowConstraint(double lb, double ub);

  /// Creates a constraint with -infinity and +infinity bounds.
  MPConstraint* MakeRowConstraint();

  /// Creates a named constraint with given bounds.
  MPConstraint* MakeRowConstraint(double lb, double ub,
                                  const std::string& name);

  /// Creates a named constraint with -infinity and +infinity bounds.
  MPConstraint* MakeRowConstraint(const std::string& name);

  /**
   * Creates a constraint owned by MPSolver enforcing:
   *     range.lower_bound() <= range.linear_expr() <= range.upper_bound()
   */
  MPConstraint* MakeRowConstraint(const LinearRange& range);

  /// As above, but also names the constraint.
  MPConstraint* MakeRowConstraint(const LinearRange& range,
                                  const std::string& name);

  /**
   * Returns the objective object.
   *
   * Note that the objective is owned by the solver, and is initialized to its
   * default value (see the MPObjective class below) at construction.
   */
  const MPObjective& Objective() const { return *objective_; }

  /// Returns the mutable objective object.
  MPObjective* MutableObjective() { return objective_.get(); }

  /**
   * The status of solving the problem. The straightforward translation to
   * homonymous enum values of MPSolverResponseStatus (see
   * ./linear_solver.proto) is guaranteed by ./enum_consistency_test.cc, you may
   * rely on it.
   */
  enum ResultStatus {
    /// optimal.
    OPTIMAL,
    /// feasible, or stopped by limit.
    FEASIBLE,
    /// proven infeasible.
    INFEASIBLE,
    /// proven unbounded.
    UNBOUNDED,
    /// abnormal, i.e., error of some kind.
    ABNORMAL,
    /// the model is trivially invalid (NaN coefficients, etc).
    MODEL_INVALID,
    /// not been solved yet.
    NOT_SOLVED = 6
  };

  /// Solves the problem using the default parameter values.
  ResultStatus Solve();

  /// Solves the problem using the specified parameter values.
  ResultStatus Solve(const MPSolverParameters& param);

  /**
   * Writes the model using the solver internal write function.  Currently only
   * available for Gurobi.
   */
  void Write(const std::string& file_name);

  /**
   * Advanced usage: compute the "activities" of all constraints, which are the
   * sums of their linear terms. The activities are returned in the same order
   * as constraints(), which is the order in which constraints were added; but
   * you can also use MPConstraint::index() to get a constraint's index.
   */
  std::vector<double> ComputeConstraintActivities() const;

  /**
   * Advanced usage: Verifies the *correctness* of the solution.
   *
   * It verifies that all variables must be within their domains, all
   * constraints must be satisfied, and the reported objective value must be
   * accurate.
   *
   * Usage:
   * - This can only be called after Solve() was called.
   * - "tolerance" is interpreted as an absolute error threshold.
   * - For the objective value only, if the absolute error is too large,
   *   the tolerance is interpreted as a relative error threshold instead.
   * - If "log_errors" is true, every single violation will be logged.
   * - If "tolerance" is negative, it will be set to infinity().
   *
   * Most users should just set the --verify_solution flag and not bother using
   * this method directly.
   */
  bool VerifySolution(double tolerance, bool log_errors) const;

  /**
   * Advanced usage: resets extracted model to solve from scratch.
   *
   * This won't reset the parameters that were set with
   * SetSolverSpecificParametersAsString() or set_time_limit() or even clear the
   * linear program. It will just make sure that next Solve() will be as if
   * everything was reconstructed from scratch.
   */
  void Reset();

  /** Interrupts the Solve() execution to terminate processing if possible.
   *
   * If the underlying interface supports interruption; it does that and returns
   * true regardless of whether there's an ongoing Solve() or not. The Solve()
   * call may still linger for a while depending on the conditions.  If
   * interruption is not supported; returns false and does nothing.
   */
  bool InterruptSolve();

  /**
   * Loads model from protocol buffer.
   *
   * Returns MPSOLVER_MODEL_IS_VALID if the model is valid, and another status
   * otherwise (currently only MPSOLVER_MODEL_INVALID and MPSOLVER_INFEASIBLE).
   * If the model isn't valid, populates "error_message".
   */
  MPSolverResponseStatus LoadModelFromProto(const MPModelProto& input_model,
                                            std::string* error_message);
  /**
   * Loads model from protocol buffer.
   *
   * The same as above, except that the loading keeps original variable and
   * constraint names. Caller should make sure that all variable names and
   * constraint names are unique, respectively.
   */
  MPSolverResponseStatus LoadModelFromProtoWithUniqueNamesOrDie(
      const MPModelProto& input_model, std::string* error_message);

  /// Encodes the current solution in a solution response protocol buffer.
  void FillSolutionResponseProto(MPSolutionResponse* response) const;

  /**
   * Solves the model encoded by a MPModelRequest protocol buffer and fills the
   * solution encoded as a MPSolutionResponse.
   *
   * Note(user): This creates a temporary MPSolver and destroys it at the end.
   * If you want to keep the MPSolver alive (for debugging, or for incremental
   * solving), you should write another version of this function that creates
   * the MPSolver object on the heap and returns it.
   */
  static void SolveWithProto(const MPModelRequest& model_request,
                             MPSolutionResponse* response);

  /// Exports model to protocol buffer.
  void ExportModelToProto(MPModelProto* output_model) const;

  /**
   * Load a solution encoded in a protocol buffer onto this solver for easy
  access via the MPSolver interface.
   *
   * IMPORTANT: This may only be used in conjunction with ExportModel(),
  following this example:
   *
   \code
     MPSolver my_solver;
     ... add variables and constraints ...
     MPModelProto model_proto;
     my_solver.ExportModelToProto(&model_proto);
     MPSolutionResponse solver_response;
     MPSolver::SolveWithProto(model_proto, &solver_response);
     if (solver_response.result_status() == MPSolutionResponse::OPTIMAL) {
       CHECK_OK(my_solver.LoadSolutionFromProto(solver_response));
       ... inspect the solution using the usual API: solution_value(), etc...
     }
  \endcode
   *
   * The response must be in OPTIMAL or FEASIBLE status.
   *
   * Returns a non-OK status if a problem arised (typically, if it wasn't used
   *     like it should be):
   * - loading a solution whose variables don't correspond to the solver's
   *   current variables
   * - loading a solution with a status other than OPTIMAL / FEASIBLE.
   *
   * Note: the objective value isn't checked. You can use VerifySolution() for
   *       that.
   */
  absl::Status LoadSolutionFromProto(
      const MPSolutionResponse& response,
      double tolerance = kDefaultPrimalTolerance);

  /**
   * Resets values of out of bound variables to the corresponding bound and
   * returns an error if any of the variables have NaN value.
   */
  absl::Status ClampSolutionWithinBounds();

  /**
   * Shortcuts to the homonymous MPModelProtoExporter methods, via exporting to
   * a MPModelProto with ExportModelToProto() (see above).
   *
   * Produces empty std::string on portable platforms (e.g. android, ios).
   */
  bool ExportModelAsLpFormat(bool obfuscate, std::string* model_str) const;
  bool ExportModelAsMpsFormat(bool fixed_format, bool obfuscate,
                              std::string* model_str) const;  

  /**
   *  Sets the number of threads to use by the underlying solver.
   *
   * Returns OkStatus if the operation was successful. num_threads must be equal
   * to or greater than 1. Note that the behaviour of this call depends on the
   * underlying solver. E.g., it may set the exact number of threads or the max
   * number of threads (check the solver's interface implementation for
   * details). Also, some solvers may not (yet) support this function, but still
   * enable multi-threading via SetSolverSpecificParametersAsString().
   */
  absl::Status SetNumThreads(int num_threads);

  /// Returns the number of threads to be used during solve.
  int GetNumThreads() const { return num_threads_; }

  /**
   * Advanced usage: pass solver specific parameters in text format.
   *
   * The format is solver-specific and is the same as the corresponding solver
   * configuration file format. Returns true if the operation was successful.
   */
  bool SetSolverSpecificParametersAsString(const std::string& parameters);
  std::string GetSolverSpecificParametersAsString() const {
    return solver_specific_parameter_string_;
  }

  /**
   * Sets a hint for solution.
   *
   * If a feasible or almost-feasible solution to the problem is already known,
   * it may be helpful to pass it to the solver so that it can be used. A solver
   * that supports this feature will try to use this information to create its
   * initial feasible solution.
   *
   * Note: It may not always be faster to give a hint like this to the
   * solver. There is also no guarantee that the solver will use this hint or
   * try to return a solution "close" to this assignment in case of multiple
   * optimal solutions.
   */
  void SetHint(std::vector<std::pair<const MPVariable*, double> > hint);

  /**
   * Advanced usage: possible basis status values for a variable and the slack
   * variable of a linear constraint.
   */
  enum BasisStatus {
    FREE = 0,
    AT_LOWER_BOUND,
    AT_UPPER_BOUND,
    FIXED_VALUE,
    BASIC
  };

  /**
   * Advanced usage: Incrementality.
   *
   * This function takes a starting basis to be used in the next LP Solve()
   * call. The statuses of a current solution can be retrieved via the
   * basis_status() function of a MPVariable or a MPConstraint.
   *
   * WARNING: With Glop, you should disable presolve when using this because
   * this information will not be modified in sync with the presolve and will
   * likely not mean much on the presolved problem.
   */
  void SetStartingLpBasis(
      const std::vector<MPSolver::BasisStatus>& variable_statuses,
      const std::vector<MPSolver::BasisStatus>& constraint_statuses);

  /**
   * Infinity.
   *
   * You can use -MPSolver::infinity() for negative infinity.
   */
  static double infinity() { return std::numeric_limits<double>::infinity(); }

  /**
   * Controls (or queries) the amount of output produced by the underlying
   * solver. The output can surface to LOGs, or to stdout or stderr, depending
   * on the implementation. The amount of output will greatly vary with each
   * implementation and each problem.
   *
   * Output is suppressed by default.
   */
  bool OutputIsEnabled() const;

  /// Enables solver logging.
  void EnableOutput();

  /// Suppresses solver logging.
  void SuppressOutput();

  absl::Duration TimeLimit() const { return time_limit_; }
  void SetTimeLimit(absl::Duration time_limit) {
    DCHECK_GE(time_limit, absl::ZeroDuration());
    time_limit_ = time_limit;
  }

  absl::Duration DurationSinceConstruction() const {
    return absl::Now() - construction_time_;
  }

  /// Returns the number of simplex iterations.
  int64 iterations() const;

  /**
   * Returns the number of branch-and-bound nodes evaluated during the solve.
   *
   * Only available for discrete problems.
   */
  int64 nodes() const;

  /// Returns a string describing the underlying solver and its version.
  std::string SolverVersion() const;

  /**
   * Advanced usage: returns the underlying solver.
   *
   * Returns the underlying solver so that the user can use solver-specific
   * features or features that are not exposed in the simple API of MPSolver.
   * This method is for advanced users, use at your own risk! In particular, if
   * you modify the model or the solution by accessing the underlying solver
   * directly, then the underlying solver will be out of sync with the
   * information kept in the wrapper (MPSolver, MPVariable, MPConstraint,
   * MPObjective). You need to cast the void* returned back to its original type
   * that depends on the interface (CBC: OsiClpSolverInterface*, CLP:
   * ClpSimplex*, GLPK: glp_prob*, SCIP: SCIP*).
   */
  void* underlying_solver();

  /** Advanced usage: computes the exact condition number of the current scaled
   * basis: L1norm(B) * L1norm(inverse(B)), where B is the scaled basis.
   *
   * This method requires that a basis exists: it should be called after Solve.
   * It is only available for continuous problems. It is implemented for GLPK
   * but not CLP because CLP does not provide the API for doing it.
   *
   * The condition number measures how well the constraint matrix is conditioned
   * and can be used to predict whether numerical issues will arise during the
   * solve: the model is declared infeasible whereas it is feasible (or
   * vice-versa), the solution obtained is not optimal or violates some
   * constraints, the resolution is slow because of repeated singularities.
   *
   * The rule of thumb to interpret the condition number kappa is:
   *   - o kappa <= 1e7: virtually no chance of numerical issues
   *   - o 1e7 < kappa <= 1e10: small chance of numerical issues
   *   - o 1e10 < kappa <= 1e13: medium chance of numerical issues
   *   - o kappa > 1e13: high chance of numerical issues
   *
   * The computation of the condition number depends on the quality of the LU
   * decomposition, so it is not very accurate when the matrix is ill
   * conditioned.
   */
  double ComputeExactConditionNumber() const;

  /**
   * Some solvers (MIP only, not LP) can produce multiple solutions to the
   * problem. Returns true when another solution is available, and updates the
   * MPVariable* objects to make the new solution queryable. Call only after
   * calling solve.
   *
   * The optimality properties of the additional solutions found, and whether or
   * not the solver computes them ahead of time or when NextSolution() is called
   * is solver specific.
   *
   * As of 2020-02-10, only Gurobi and SCIP support NextSolution(), see
   * linear_solver_interfaces_test for an example of how to configure these
   * solvers for multiple solutions. Other solvers return false unconditionally.
   */
  ABSL_MUST_USE_RESULT bool NextSolution();

  // Does not take ownership of "mp_callback".
  //
  // As of 2019-10-22, only SCIP and Gurobi support Callbacks.
  // SCIP does not support suggesting a heuristic solution in the callback.
  //
  // See go/mpsolver-callbacks for additional documentation.
  void SetCallback(MPCallback* mp_callback);
  bool SupportsCallbacks() const;

  // DEPRECATED: Use TimeLimit() and SetTimeLimit(absl::Duration) instead.
  // NOTE: These deprecated functions used the convention time_limit = 0 to mean
  // "no limit", which now corresponds to time_limit_ = InfiniteDuration().
  int64 time_limit() const {
    return time_limit_ == absl::InfiniteDuration()
               ? 0
               : absl::ToInt64Milliseconds(time_limit_);
  }
  void set_time_limit(int64 time_limit_milliseconds) {
    SetTimeLimit(time_limit_milliseconds == 0
                     ? absl::InfiniteDuration()
                     : absl::Milliseconds(time_limit_milliseconds));
  }
  double time_limit_in_secs() const {
    return static_cast<double>(time_limit()) / 1000.0;
  }

  // DEPRECATED: Use DurationSinceConstruction() instead.
  int64 wall_time() const {
    return absl::ToInt64Milliseconds(DurationSinceConstruction());
  }

796 797 798 799
  // Supports search and loading Gurobi shared library.
  static bool LoadGurobiSharedLibrary();
  static void SetGurobiLibraryPath(const std::string &full_library_path);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
  friend class GLPKInterface;
  friend class CLPInterface;
  friend class CBCInterface;
  friend class SCIPInterface;
  friend class GurobiInterface;
  friend class CplexInterface;
  friend class XpressInterface;
  friend class SLMInterface;
  friend class MPSolverInterface;
  friend class GLOPInterface;
  friend class BopInterface;
  friend class SatInterface;
  friend class KnapsackInterface;

  // Debugging: verify that the given MPVariable* belongs to this solver.
  bool OwnsVariable(const MPVariable* var) const;

 private:
  // Computes the size of the constraint with the largest number of
  // coefficients with index in [min_constraint_index,
  // max_constraint_index)
  int ComputeMaxConstraintSize(int min_constraint_index,
                               int max_constraint_index) const;

  // Returns true if the model has constraints with lower bound > upper bound.
  bool HasInfeasibleConstraints() const;

  // Returns true if the model has at least 1 integer variable.
  bool HasIntegerVariables() const;

  // Generates the map from variable names to their indices.
  void GenerateVariableNameIndex() const;

  // Generates the map from constraint names to their indices.
  void GenerateConstraintNameIndex() const;

836 837 838 839
  // Checks licenses for commercial solver, and checks shared library loading
  // for or-tools.
  static bool GurobiIsCorrectlyInstalled();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
  // The name of the linear programming problem.
  const std::string name_;

  // The type of the linear programming problem.
  const OptimizationProblemType problem_type_;

  // The solver interface.
  std::unique_ptr<MPSolverInterface> interface_;

  // The vector of variables in the problem.
  std::vector<MPVariable*> variables_;
  // A map from a variable's name to its index in variables_.
  mutable absl::optional<absl::flat_hash_map<std::string, int> >
      variable_name_to_index_;
  // Whether variables have been extracted to the underlying interface.
  std::vector<bool> variable_is_extracted_;

  // The vector of constraints in the problem.
  std::vector<MPConstraint*> constraints_;
  // A map from a constraint's name to its index in constraints_.
  mutable absl::optional<absl::flat_hash_map<std::string, int> >
      constraint_name_to_index_;
  // Whether constraints have been extracted to the underlying interface.
  std::vector<bool> constraint_is_extracted_;

  // The linear objective function.
  std::unique_ptr<MPObjective> objective_;

  // Initial values for all or some of the problem variables that can be
  // exploited as a starting hint by a solver.
  //
  // Note(user): as of 05/05/2015, we can't use >> because of some SWIG errors.
  //
  // TODO(user): replace by two vectors, a std::vector<bool> to indicate if a
  // hint is provided and a std::vector<double> for the hint value.
  std::vector<std::pair<const MPVariable*, double> > solution_hint_;

  absl::Duration time_limit_ = absl::InfiniteDuration();  // Default = No limit.

  const absl::Time construction_time_;

  // Permanent storage for the number of threads.
  int num_threads_ = 1;

  // Permanent storage for SetSolverSpecificParametersAsString().
  std::string solver_specific_parameter_string_;

  MPSolverResponseStatus LoadModelFromProtoInternal(
      const MPModelProto& input_model, bool clear_names,
      bool check_model_validity, std::string* error_message);

  DISALLOW_COPY_AND_ASSIGN(MPSolver);
};

inline bool SolverTypeIsMip(MPSolver::OptimizationProblemType solver_type) {
  return SolverTypeIsMip(static_cast<MPModelRequest::SolverType>(solver_type));
}

const absl::string_view ToString(
    MPSolver::OptimizationProblemType optimization_problem_type);

inline std::ostream& operator<<(
    std::ostream& os,
    MPSolver::OptimizationProblemType optimization_problem_type) {
  return os << ToString(optimization_problem_type);
}

inline std::ostream& operator<<(std::ostream& os,
                                MPSolver::ResultStatus status) {
  return os << ProtoEnumToString<MPSolverResponseStatus>(
             static_cast<MPSolverResponseStatus>(status));
}

bool AbslParseFlag(absl::string_view text,
                   MPSolver::OptimizationProblemType* solver_type,
                   std::string* error);

inline std::string AbslUnparseFlag(
    MPSolver::OptimizationProblemType solver_type) {
  return std::string(ToString(solver_type));
}

/// A class to express a linear objective.
class MPObjective {
 public:
  /**
   *  Clears the offset, all variables and coefficients, and the optimization
   * direction.
   */
  void Clear();

  /**
   * Sets the coefficient of the variable in the objective.
   *
   * If the variable does not belong to the solver, the function just returns,
   * or crashes in non-opt mode.
   */
  void SetCoefficient(const MPVariable* const var, double coeff);

  /**
   *  Gets the coefficient of a given variable in the objective
   *
   * It returns 0 if the variable does not appear in the objective).
   */
  double GetCoefficient(const MPVariable* const var) const;

  /**
   * Returns a map from variables to their coefficients in the objective.
   *
   * If a variable is not present in the map, then its coefficient is zero.
   */
  const absl::flat_hash_map<const MPVariable*, double>& terms() const {
    return coefficients_;
  }

  /// Sets the constant term in the objective.
  void SetOffset(double value);

  /// Gets the constant term in the objective.
  double offset() const { return offset_; }

  /**
   * Resets the current objective to take the value of linear_expr, and sets the
   * objective direction to maximize if "is_maximize", otherwise minimizes.
   */
  void OptimizeLinearExpr(const LinearExpr& linear_expr, bool is_maximization);

  /// Resets the current objective to maximize linear_expr.
  void MaximizeLinearExpr(const LinearExpr& linear_expr) {
    OptimizeLinearExpr(linear_expr, true);
  }
  /// Resets the current objective to minimize linear_expr.
  void MinimizeLinearExpr(const LinearExpr& linear_expr) {
    OptimizeLinearExpr(linear_expr, false);
  }

  /// Adds linear_expr to the current objective, does not change the direction.
  void AddLinearExpr(const LinearExpr& linear_expr);

  /// Sets the optimization direction (maximize: true or minimize: false).
  void SetOptimizationDirection(bool maximize);

  /// Sets the optimization direction to minimize.
  void SetMinimization() { SetOptimizationDirection(false); }

  /// Sets the optimization direction to maximize.
  void SetMaximization() { SetOptimizationDirection(true); }

  /// Is the optimization direction set to maximize?
  bool maximization() const;

  /// Is the optimization direction set to minimize?
  bool minimization() const;

  /**
   * Returns the objective value of the best solution found so far.
   *
   * It is the optimal objective value if the problem has been solved to
   * optimality.
   *
   * Note: the objective value may be slightly different than what you could
   * compute yourself using \c MPVariable::solution_value(); please use the
   * --verify_solution flag to gain confidence about the numerical stability of
   * your solution.
   */
  double Value() const;

  /**
   * Returns the best objective bound.
   *
   * In case of minimization, it is a lower bound on the objective value of the
   * optimal integer solution. Only available for discrete problems.
   */
  double BestBound() const;

 private:
  friend class MPSolver;
  friend class MPSolverInterface;
  friend class CBCInterface;
  friend class CLPInterface;
  friend class GLPKInterface;
  friend class SCIPInterface;
  friend class SLMInterface;
  friend class GurobiInterface;
  friend class CplexInterface;
  friend class XpressInterface;
  friend class GLOPInterface;
  friend class BopInterface;
  friend class SatInterface;
  friend class KnapsackInterface;

  // Constructor. An objective points to a single MPSolverInterface
  // that is specified in the constructor. An objective cannot belong
  // to several models.
  // At construction, an MPObjective has no terms (which is equivalent
  // on having a coefficient of 0 for all variables), and an offset of 0.
  explicit MPObjective(MPSolverInterface* const interface_in)
      : interface_(interface_in), coefficients_(1), offset_(0.0) {}

  MPSolverInterface* const interface_;

  // Mapping var -> coefficient.
  absl::flat_hash_map<const MPVariable*, double> coefficients_;
  // Constant term.
  double offset_;

  DISALLOW_COPY_AND_ASSIGN(MPObjective);
};

/// The class for variables of a Mathematical Programming (MP) model.
class MPVariable {
 public:
  /// Returns the name of the variable.
  const std::string& name() const { return name_; }

  /// Sets the integrality requirement of the variable.
  void SetInteger(bool integer);

  /// Returns the integrality requirement of the variable.
  bool integer() const { return integer_; }

  /**
   * Returns the value of the variable in the current solution.
   *
   * If the variable is integer, then the value will always be an integer (the
   * underlying solver handles floating-point values only, but this function
   * automatically rounds it to the nearest integer; see: man 3 round).
   */
  double solution_value() const;

  /// Returns the index of the variable in the MPSolver::variables_.
  int index() const { return index_; }

  /// Returns the lower bound.
  double lb() const { return lb_; }

  /// Returns the upper bound.
  double ub() const { return ub_; }

  /// Sets the lower bound.
  void SetLB(double lb) { SetBounds(lb, ub_); }

  /// Sets the upper bound.
  void SetUB(double ub) { SetBounds(lb_, ub); }

  /// Sets both the lower and upper bounds.
  void SetBounds(double lb, double ub);

  /**
   * Advanced usage: unrounded solution value.
   *
   * The returned value won't be rounded to the nearest integer even if the
   * variable is integer.
   */
  double unrounded_solution_value() const;

  /**
   * Advanced usage: returns the reduced cost of the variable in the current
   * solution (only available for continuous problems).
   */
  double reduced_cost() const;

  /**
   * Advanced usage: returns the basis status of the variable in the current
   * solution (only available for continuous problems).
1105 1106
   *
   * @see MPSolver::BasisStatus.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1107 1108 1109
   */
  MPSolver::BasisStatus basis_status() const;

1110 1111
  /** Returns the branching priority, or 0 if it was not set. */
  int branching_priority() const { return branching_priority_; }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
  /**
   * Advanced usage: Certain MIP solvers (e.g. Gurobi or SCIP) allow you to set
   * a per-variable priority for determining which variable to branch on.
   *
   * A value of 0 is treated as default, and is equivalent to not setting the
   * branching priority. The solver looks first to branch on fractional
   * variables in higher priority levels. As of 2019-05, only Gurobi and SCIP
   * support setting branching priority; all other solvers will simply ignore
   * this annotation.
   */
  void SetBranchingPriority(int priority);

 protected:
  friend class MPSolver;
  friend class MPSolverInterface;
  friend class CBCInterface;
  friend class CLPInterface;
  friend class GLPKInterface;
  friend class SCIPInterface;
  friend class SLMInterface;
  friend class GurobiInterface;
  friend class CplexInterface;
  friend class XpressInterface;
  friend class GLOPInterface;
  friend class MPVariableSolutionValueTest;
  friend class BopInterface;
  friend class SatInterface;
  friend class KnapsackInterface;

  // Constructor. A variable points to a single MPSolverInterface that
  // is specified in the constructor. A variable cannot belong to
  // several models.
  MPVariable(int index, double lb, double ub, bool integer,
             const std::string& name, MPSolverInterface* const interface_in)
      : index_(index),
        lb_(lb),
        ub_(ub),
        integer_(integer),
        name_(name.empty() ? absl::StrFormat("auto_v_%09d", index) : name),
        solution_value_(0.0),
        reduced_cost_(0.0),
        interface_(interface_in) {}

  void set_solution_value(double value) { solution_value_ = value; }
  void set_reduced_cost(double reduced_cost) { reduced_cost_ = reduced_cost; }

 private:
  const int index_;
  double lb_;
  double ub_;
  bool integer_;
  const std::string name_;
  double solution_value_;
  double reduced_cost_;
  int branching_priority_ = 0;
  MPSolverInterface* const interface_;
  DISALLOW_COPY_AND_ASSIGN(MPVariable);
};

/**
 * The class for constraints of a Mathematical Programming (MP) model.
 *
 * A constraint is represented as a linear equation or inequality.
 */
class MPConstraint {
 public:
  /// Returns the name of the constraint.
  const std::string& name() const { return name_; }

  /// Clears all variables and coefficients. Does not clear the bounds.
  void Clear();

  /**
   * Sets the coefficient of the variable on the constraint.
   *
   * If the variable does not belong to the solver, the function just returns,
   * or crashes in non-opt mode.
   */
  void SetCoefficient(const MPVariable* const var, double coeff);

  /**
   * Gets the coefficient of a given variable on the constraint (which is 0 if
   * the variable does not appear in the constraint).
   */
  double GetCoefficient(const MPVariable* const var) const;

  /**
   * Returns a map from variables to their coefficients in the constraint.
   *
   * If a variable is not present in the map, then its coefficient is zero.
   */
  const absl::flat_hash_map<const MPVariable*, double>& terms() const {
    return coefficients_;
  }

  /// Returns the lower bound.
  double lb() const { return lb_; }

  /// Returns the upper bound.
  double ub() const { return ub_; }

  /// Sets the lower bound.
  void SetLB(double lb) { SetBounds(lb, ub_); }

  /// Sets the upper bound.
  void SetUB(double ub) { SetBounds(lb_, ub); }

  /// Sets both the lower and upper bounds.
  void SetBounds(double lb, double ub);

  /// Advanced usage: returns true if the constraint is "lazy" (see below).
  bool is_lazy() const { return is_lazy_; }

  /**
   * Advanced usage: sets the constraint "laziness".
   *
   * <em>This is only supported for SCIP and has no effect on other
   * solvers.</em>
   *
   * When \b laziness is true, the constraint is only considered by the Linear
   * Programming solver if its current solution violates the constraint. In this
   * case, the constraint is definitively added to the problem. This may be
   * useful in some MIP problems, and may have a dramatic impact on performance.
   *
   * For more info see: http://tinyurl.com/lazy-constraints.
   */
  void set_is_lazy(bool laziness) { is_lazy_ = laziness; }

  const MPVariable* indicator_variable() const { return indicator_variable_; }
  bool indicator_value() const { return indicator_value_; }

  /// Returns the index of the constraint in the MPSolver::constraints_.
  int index() const { return index_; }

  /**
   * Advanced usage: returns the dual value of the constraint in the current
   * solution (only available for continuous problems).
   */
  double dual_value() const;

  /**
   * Advanced usage: returns the basis status of the constraint.
   *
   * It is only available for continuous problems).
   *
   * Note that if a constraint "linear_expression in [lb, ub]" is transformed
   * into "linear_expression + slack = 0" with slack in [-ub, -lb], then this
   * status is the same as the status of the slack variable with AT_UPPER_BOUND
   * and AT_LOWER_BOUND swapped.
1261 1262
   *
   * @see MPSolver::BasisStatus.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810
   */
  MPSolver::BasisStatus basis_status() const;

 protected:
  friend class MPSolver;
  friend class MPSolverInterface;
  friend class CBCInterface;
  friend class CLPInterface;
  friend class GLPKInterface;
  friend class SCIPInterface;
  friend class SLMInterface;
  friend class GurobiInterface;
  friend class CplexInterface;
  friend class XpressInterface;
  friend class GLOPInterface;
  friend class BopInterface;
  friend class SatInterface;
  friend class KnapsackInterface;

  // Constructor. A constraint points to a single MPSolverInterface
  // that is specified in the constructor. A constraint cannot belong
  // to several models.
  MPConstraint(int index, double lb, double ub, const std::string& name,
               MPSolverInterface* const interface_in)
      : coefficients_(1),
        index_(index),
        lb_(lb),
        ub_(ub),
        name_(name.empty() ? absl::StrFormat("auto_c_%09d", index) : name),
        is_lazy_(false),
        indicator_variable_(nullptr),
        dual_value_(0.0),
        interface_(interface_in) {}

  void set_dual_value(double dual_value) { dual_value_ = dual_value; }

 private:
  // Returns true if the constraint contains variables that have not
  // been extracted yet.
  bool ContainsNewVariables();

  // Mapping var -> coefficient.
  absl::flat_hash_map<const MPVariable*, double> coefficients_;

  const int index_;  // See index().

  // The lower bound for the linear constraint.
  double lb_;

  // The upper bound for the linear constraint.
  double ub_;

  // Name.
  const std::string name_;

  // True if the constraint is "lazy", i.e. the constraint is added to the
  // underlying Linear Programming solver only if it is violated.
  // By default this parameter is 'false'.
  bool is_lazy_;

  // If given, this constraint is only active if `indicator_variable_`'s value
  // is equal to `indicator_value_`.
  const MPVariable* indicator_variable_;
  bool indicator_value_;

  double dual_value_;
  MPSolverInterface* const interface_;
  DISALLOW_COPY_AND_ASSIGN(MPConstraint);
};

/**
 * This class stores parameter settings for LP and MIP solvers. Some parameters
 * are marked as advanced: do not change their values unless you know what you
 * are doing!
 *
 * For developers: how to add a new parameter:
 * - Add the new Foo parameter in the DoubleParam or IntegerParam enum.
 * - If it is a categorical param, add a FooValues enum.
 * - Decide if the wrapper should define a default value for it: yes
 *   if it controls the properties of the solution (example:
 *   tolerances) or if it consistently improves performance, no
 *   otherwise. If yes, define kDefaultFoo.
 * - Add a foo_value_ member and, if no default value is defined, a
 *   foo_is_default_ member.
 * - Add code to handle Foo in Set...Param, Reset...Param,
 *   Get...Param, Reset and the constructor.
 * - In class MPSolverInterface, add a virtual method SetFoo, add it
 *   to SetCommonParameters or SetMIPParameters, and implement it for
 *   each solver. Sometimes, parameters need to be implemented
 *   differently, see for example the INCREMENTALITY implementation.
 * - Add a test in linear_solver_test.cc.
 *
 * TODO(user): store the parameter values in a protocol buffer
 * instead. We need to figure out how to deal with the subtleties of
 * the default values.
 */
class MPSolverParameters {
 public:
  /// Enumeration of parameters that take continuous values.
  enum DoubleParam {
    /// Limit for relative MIP gap.
    RELATIVE_MIP_GAP = 0,

    /**
     * Advanced usage: tolerance for primal feasibility of basic solutions.
     *
     * This does not control the integer feasibility tolerance of integer
     * solutions for MIP or the tolerance used during presolve.
     */
    PRIMAL_TOLERANCE = 1,
    /// Advanced usage: tolerance for dual feasibility of basic solutions.
    DUAL_TOLERANCE = 2
  };

  /// Enumeration of parameters that take integer or categorical values.
  enum IntegerParam {
    /// Advanced usage: presolve mode.
    PRESOLVE = 1000,
    /// Algorithm to solve linear programs.
    LP_ALGORITHM = 1001,
    /// Advanced usage: incrementality from one solve to the next.
    INCREMENTALITY = 1002,
    /// Advanced usage: enable or disable matrix scaling.
    SCALING = 1003
  };

  /// For each categorical parameter, enumeration of possible values.
  enum PresolveValues {
    /// Presolve is off.
    PRESOLVE_OFF = 0,
    /// Presolve is on.
    PRESOLVE_ON = 1
  };

  /// LP algorithm to use.
  enum LpAlgorithmValues {
    /// Dual simplex.
    DUAL = 10,
    /// Primal simplex.
    PRIMAL = 11,
    /// Barrier algorithm.
    BARRIER = 12
  };

  /// Advanced usage: Incrementality options.
  enum IncrementalityValues {
    /// Start solve from scratch.
    INCREMENTALITY_OFF = 0,

    /**
     * Reuse results from previous solve as much as the underlying solver
     * allows.
     */
    INCREMENTALITY_ON = 1
  };

  /// Advanced usage: Scaling options.
  enum ScalingValues {
    /// Scaling is off.
    SCALING_OFF = 0,
    /// Scaling is on.
    SCALING_ON = 1
  };

  // Placeholder value to indicate that a parameter is set to
  // the default value defined in the wrapper.
  static const double kDefaultDoubleParamValue;
  static const int kDefaultIntegerParamValue;

  // Placeholder value to indicate that a parameter is unknown.
  static const double kUnknownDoubleParamValue;
  static const int kUnknownIntegerParamValue;

  // Default values for parameters. Only parameters that define the
  // properties of the solution returned need to have a default value
  // (that is the same for all solvers). You can also define a default
  // value for performance parameters when you are confident it is a
  // good choice (example: always turn presolve on).
  static const double kDefaultRelativeMipGap;
  static const double kDefaultPrimalTolerance;
  static const double kDefaultDualTolerance;
  static const PresolveValues kDefaultPresolve;
  static const IncrementalityValues kDefaultIncrementality;

  /// The constructor sets all parameters to their default value.
  MPSolverParameters();

  /// Sets a double parameter to a specific value.
  void SetDoubleParam(MPSolverParameters::DoubleParam param, double value);

  /// Sets a integer parameter to a specific value.
  void SetIntegerParam(MPSolverParameters::IntegerParam param, int value);

  /**
   * Sets a double parameter to its default value (default value defined in
   * MPSolverParameters if it exists, otherwise the default value defined in
   * the underlying solver).
   */
  void ResetDoubleParam(MPSolverParameters::DoubleParam param);

  /**
   * Sets an integer parameter to its default value (default value defined in
   * MPSolverParameters if it exists, otherwise the default value defined in
   * the underlying solver).
   */
  void ResetIntegerParam(MPSolverParameters::IntegerParam param);

  /// Sets all parameters to their default value.
  void Reset();

  /// Returns the value of a double parameter.
  double GetDoubleParam(MPSolverParameters::DoubleParam param) const;

  /// Returns the value of an integer parameter.
  int GetIntegerParam(MPSolverParameters::IntegerParam param) const;

 private:
  // Parameter value for each parameter.
  // @see DoubleParam
  // @see IntegerParam
  double relative_mip_gap_value_;
  double primal_tolerance_value_;
  double dual_tolerance_value_;
  int presolve_value_;
  int scaling_value_;
  int lp_algorithm_value_;
  int incrementality_value_;

  // Boolean value indicating whether each parameter is set to the
  // solver's default value. Only parameters for which the wrapper
  // does not define a default value need such an indicator.
  bool lp_algorithm_is_default_;

  DISALLOW_COPY_AND_ASSIGN(MPSolverParameters);
};

// Whether the given MPSolverResponseStatus (of a solve) would yield an RPC
// error when happening on the linear solver stubby server, see
// ./linear_solver_service.proto.
// Note that RPC errors forbid to carry a response to the client, who can only
// see the RPC error itself (error code + error message).
bool MPSolverResponseStatusIsRpcError(MPSolverResponseStatus status);

// This class wraps the actual mathematical programming solvers. Each
// solver (GLOP, CLP, CBC, GLPK, SCIP) has its own interface class that
// derives from this abstract class. This class is never directly
// accessed by the user.
// @see glop_interface.cc
// @see cbc_interface.cc
// @see clp_interface.cc
// @see glpk_interface.cc
// @see scip_interface.cc
class MPSolverInterface {
 public:
  enum SynchronizationStatus {
    // The underlying solver (CLP, GLPK, ...) and MPSolver are not in
    // sync for the model nor for the solution.
    MUST_RELOAD,
    // The underlying solver and MPSolver are in sync for the model
    // but not for the solution: the model has changed since the
    // solution was computed last.
    MODEL_SYNCHRONIZED,
    // The underlying solver and MPSolver are in sync for the model and
    // the solution.
    SOLUTION_SYNCHRONIZED
  };

  // When the underlying solver does not provide the number of simplex
  // iterations.
  static constexpr int64 kUnknownNumberOfIterations = -1;
  // When the underlying solver does not provide the number of
  // branch-and-bound nodes.
  static constexpr int64 kUnknownNumberOfNodes = -1;

  // Constructor. The user will access the MPSolverInterface through the
  // MPSolver passed as argument.
  explicit MPSolverInterface(MPSolver* const solver);
  virtual ~MPSolverInterface();

  // ----- Solve -----
  // Solves problem with specified parameter values. Returns true if the
  // solution is optimal.
  virtual MPSolver::ResultStatus Solve(const MPSolverParameters& param) = 0;

  // Directly solves a MPModelRequest, bypassing the MPSolver data structures
  // entirely. Returns {} (eg. absl::nullopt) if the feature is not supported by
  // the underlying solver.
  virtual absl::optional<MPSolutionResponse> DirectlySolveProto(
      const MPModelRequest& request) {
    return absl::nullopt;
  }

  // Writes the model using the solver internal write function.  Currently only
  // available for GurobiInterface.
  virtual void Write(const std::string& filename);

  // ----- Model modifications and extraction -----
  // Resets extracted model.
  virtual void Reset() = 0;

  // Sets the optimization direction (min/max).
  virtual void SetOptimizationDirection(bool maximize) = 0;

  // Modifies bounds of an extracted variable.
  virtual void SetVariableBounds(int index, double lb, double ub) = 0;

  // Modifies integrality of an extracted variable.
  virtual void SetVariableInteger(int index, bool integer) = 0;

  // Modify bounds of an extracted variable.
  virtual void SetConstraintBounds(int index, double lb, double ub) = 0;

  // Adds a linear constraint.
  virtual void AddRowConstraint(MPConstraint* const ct) = 0;

  // Adds an indicator constraint. Returns true if the feature is supported by
  // the underlying solver.
  virtual bool AddIndicatorConstraint(MPConstraint* const ct) {
    LOG(ERROR) << "Solver doesn't support indicator constraints.";
    return false;
  }

  // Add a variable.
  virtual void AddVariable(MPVariable* const var) = 0;

  // Changes a coefficient in a constraint.
  virtual void SetCoefficient(MPConstraint* const constraint,
                              const MPVariable* const variable,
                              double new_value, double old_value) = 0;

  // Clears a constraint from all its terms.
  virtual void ClearConstraint(MPConstraint* const constraint) = 0;

  // Changes a coefficient in the linear objective.
  virtual void SetObjectiveCoefficient(const MPVariable* const variable,
                                       double coefficient) = 0;

  // Changes the constant term in the linear objective.
  virtual void SetObjectiveOffset(double value) = 0;

  // Clears the objective from all its terms.
  virtual void ClearObjective() = 0;

  virtual void BranchingPriorityChangedForVariable(int var_index) {}
  // ------ Query statistics on the solution and the solve ------
  // Returns the number of simplex iterations. The problem must be discrete,
  // otherwise it crashes, or returns kUnknownNumberOfIterations in NDEBUG mode.
  virtual int64 iterations() const = 0;
  // Returns the number of branch-and-bound nodes. The problem must be discrete,
  // otherwise it crashes, or returns kUnknownNumberOfNodes in NDEBUG mode.
  virtual int64 nodes() const = 0;
  // Returns the best objective bound. The problem must be discrete, otherwise
  // it crashes, or returns trivial_worst_objective_bound() in NDEBUG mode.
  virtual double best_objective_bound() const = 0;
  // A trivial objective bound: the worst possible value of the objective,
  // which will be +infinity if minimizing and -infinity if maximing.
  double trivial_worst_objective_bound() const;
  // Returns the objective value of the best solution found so far.
  double objective_value() const;

  // Returns the basis status of a row.
  virtual MPSolver::BasisStatus row_status(int constraint_index) const = 0;
  // Returns the basis status of a constraint.
  virtual MPSolver::BasisStatus column_status(int variable_index) const = 0;

  // Checks whether the solution is synchronized with the model, i.e. whether
  // the model has changed since the solution was computed last.
  // If it isn't, it crashes in NDEBUG, and returns false othwerwise.
  bool CheckSolutionIsSynchronized() const;
  // Checks whether a feasible solution exists. The behavior is similar to
  // CheckSolutionIsSynchronized() above.
  virtual bool CheckSolutionExists() const;
  // Handy shortcut to do both checks above (it is often used).
  bool CheckSolutionIsSynchronizedAndExists() const {
    return CheckSolutionIsSynchronized() && CheckSolutionExists();
  }
  // Checks whether information on the best objective bound exists. The behavior
  // is similar to CheckSolutionIsSynchronized() above.
  virtual bool CheckBestObjectiveBoundExists() const;

  // ----- Misc -----
  // Queries problem type. For simplicity, the distinction between
  // continuous and discrete is based on the declaration of the user
  // when the solver is created (example: GLPK_LINEAR_PROGRAMMING
  // vs. GLPK_MIXED_INTEGER_PROGRAMMING), not on the actual content of
  // the model.
  // Returns true if the problem is continuous.
  virtual bool IsContinuous() const = 0;
  // Returns true if the problem is continuous and linear.
  virtual bool IsLP() const = 0;
  // Returns true if the problem is discrete and linear.
  virtual bool IsMIP() const = 0;

  // Returns the index of the last variable extracted.
  int last_variable_index() const { return last_variable_index_; }

  bool variable_is_extracted(int var_index) const {
    return solver_->variable_is_extracted_[var_index];
  }
  void set_variable_as_extracted(int var_index, bool extracted) {
    solver_->variable_is_extracted_[var_index] = extracted;
  }
  bool constraint_is_extracted(int ct_index) const {
    return solver_->constraint_is_extracted_[ct_index];
  }
  void set_constraint_as_extracted(int ct_index, bool extracted) {
    solver_->constraint_is_extracted_[ct_index] = extracted;
  }

  // Returns the boolean indicating the verbosity of the solver output.
  bool quiet() const { return quiet_; }
  // Sets the boolean indicating the verbosity of the solver output.
  void set_quiet(bool quiet_value) { quiet_ = quiet_value; }

  // Returns the result status of the last solve.
  MPSolver::ResultStatus result_status() const {
    CheckSolutionIsSynchronized();
    return result_status_;
  }

  // Returns a string describing the underlying solver and its version.
  virtual std::string SolverVersion() const = 0;

  // Returns the underlying solver.
  virtual void* underlying_solver() = 0;

  // Computes exact condition number. Only available for continuous
  // problems and only implemented in GLPK.
  virtual double ComputeExactConditionNumber() const;

  // See MPSolver::SetStartingLpBasis().
  virtual void SetStartingLpBasis(
      const std::vector<MPSolver::BasisStatus>& variable_statuses,
      const std::vector<MPSolver::BasisStatus>& constraint_statuses) {
    LOG(FATAL) << "Not supported by this solver.";
  }

  virtual bool InterruptSolve() { return false; }

  // See MPSolver::NextSolution() for contract.
  virtual bool NextSolution() { return false; }

  // See MPSolver::SetCallback() for details.
  virtual void SetCallback(MPCallback* mp_callback) {
    LOG(FATAL) << "Callbacks not supported for this solver.";
  }

  virtual bool SupportsCallbacks() const { return false; }

  friend class MPSolver;

  // To access the maximize_ bool and the MPSolver.
  friend class MPConstraint;
  friend class MPObjective;

 protected:
  MPSolver* const solver_;
  // Indicates whether the model and the solution are synchronized.
  SynchronizationStatus sync_status_;
  // Indicates whether the solve has reached optimality,
  // infeasibility, a limit, etc.
  MPSolver::ResultStatus result_status_;
  // Optimization direction.
  bool maximize_;

  // Index in MPSolver::variables_ of last constraint extracted.
  int last_constraint_index_;
  // Index in MPSolver::constraints_ of last variable extracted.
  int last_variable_index_;

  // The value of the objective function.
  double objective_value_;

  // Boolean indicator for the verbosity of the solver output.
  bool quiet_;

  // Index of dummy variable created for empty constraints or the
  // objective offset.
  static const int kDummyVariableIndex;

  // Extracts model stored in MPSolver.
  void ExtractModel();
  // Extracts the variables that have not been extracted yet.
  virtual void ExtractNewVariables() = 0;
  // Extracts the constraints that have not been extracted yet.
  virtual void ExtractNewConstraints() = 0;
  // Extracts the objective.
  virtual void ExtractObjective() = 0;
  // Resets the extraction information.
  void ResetExtractionInformation();
  // Change synchronization status from SOLUTION_SYNCHRONIZED to
  // MODEL_SYNCHRONIZED. To be used for model changes.
  void InvalidateSolutionSynchronization();

  // Sets parameters common to LP and MIP in the underlying solver.
  void SetCommonParameters(const MPSolverParameters& param);
  // Sets MIP specific parameters in the underlying solver.
  void SetMIPParameters(const MPSolverParameters& param);
  // Sets all parameters in the underlying solver.
  virtual void SetParameters(const MPSolverParameters& param) = 0;
  // Sets an unsupported double parameter.
  void SetUnsupportedDoubleParam(MPSolverParameters::DoubleParam param);
  // Sets an unsupported integer parameter.
  virtual void SetUnsupportedIntegerParam(
      MPSolverParameters::IntegerParam param);
  // Sets a supported double parameter to an unsupported value.
  void SetDoubleParamToUnsupportedValue(MPSolverParameters::DoubleParam param,
                                        double value);
  // Sets a supported integer parameter to an unsupported value.
  virtual void SetIntegerParamToUnsupportedValue(
      MPSolverParameters::IntegerParam param, int value);
  // Sets each parameter in the underlying solver.
  virtual void SetRelativeMipGap(double value) = 0;
  virtual void SetPrimalTolerance(double value) = 0;
  virtual void SetDualTolerance(double value) = 0;
  virtual void SetPresolveMode(int value) = 0;

  // Sets the number of threads to be used by the solver.
  virtual absl::Status SetNumThreads(int num_threads);

  // Pass solver specific parameters in text format. The format is
  // solver-specific and is the same as the corresponding solver configuration
  // file format. Returns true if the operation was successful.
  //
  // The default implementation of this method stores the parameters in a
  // temporary file and calls ReadParameterFile to import the parameter file
  // into the solver. Solvers that support passing the parameters directly can
  // override this method to skip the temporary file logic.
  virtual bool SetSolverSpecificParametersAsString(
      const std::string& parameters);

  // Reads a solver-specific file of parameters and set them.
  // Returns true if there was no errors.
  virtual bool ReadParameterFile(const std::string& filename);

  // Returns a file extension like ".tmp", this is needed because some solvers
  // require a given extension for the ReadParameterFile() filename and we need
  // to know it to generate a temporary parameter file.
  virtual std::string ValidFileExtensionForParameterFile() const;

  // Sets the scaling mode.
  virtual void SetScalingMode(int value) = 0;
  virtual void SetLpAlgorithm(int value) = 0;
};

}  // namespace operations_research

#endif  // OR_TOOLS_LINEAR_SOLVER_LINEAR_SOLVER_H_