forked from google/or-tools
-
Notifications
You must be signed in to change notification settings - Fork 3
/
linear_solver.h
1808 lines (1556 loc) · 66.2 KB
/
linear_solver.h
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
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
796
797
798
799
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
836
837
838
839
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
// 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"
ABSL_DECLARE_FLAG(bool, linear_solver_enable_verbose_output);
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 {
// Linear programming problems.
// ----------------------------
CLP_LINEAR_PROGRAMMING = 0,
GLPK_LINEAR_PROGRAMMING = 1,
GLOP_LINEAR_PROGRAMMING = 2, // Recommended default value. Made in Google.
// Integer programming problems.
// -----------------------------
SCIP_MIXED_INTEGER_PROGRAMMING = 3, // Recommended default value.
GLPK_MIXED_INTEGER_PROGRAMMING = 4,
CBC_MIXED_INTEGER_PROGRAMMING = 5,
// Commercial software (need license).
GUROBI_LINEAR_PROGRAMMING = 6,
GUROBI_MIXED_INTEGER_PROGRAMMING = 7,
CPLEX_LINEAR_PROGRAMMING = 10,
CPLEX_MIXED_INTEGER_PROGRAMMING = 11,
XPRESS_LINEAR_PROGRAMMING = 101,
XPRESS_MIXED_INTEGER_PROGRAMMING = 102,
// 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,
};
/// Create a solver with the given name and underlying solver backend.
MPSolver(const std::string& name, OptimizationProblemType problem_type);
virtual ~MPSolver();
/**
* 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& solver_id);
/**
* 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.
* See the documentation of CreateSolver() for the list of supported names.
*/
static bool ParseSolverType(absl::string_view solver_id,
OptimizationProblemType* type);
/**
* Parses the name of the solver and returns the correct optimization type or
* dies. Invariant: ParseSolverTypeOrDie(ToString(type)) = type.
*/
static OptimizationProblemType ParseSolverTypeOrDie(
const std::string& solver_id);
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.
*
* Note(pawell): This attempts to first use `DirectlySolveProto()` (if
* implemented). Consequently, this most likely does *not* override any of
* the default parameters of the underlying solver. This behavior *differs*
* from `MPSolver::Solve()` which by default sets the feasibility tolerance
* and the gap limit (as of 2020/02/11, to 1e-7 and 0.0001, respectively).
*/
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());
}
// Supports search and loading Gurobi shared library.
static bool LoadGurobiSharedLibrary();
static void SetGurobiLibraryPath(const std::string& full_library_path);
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;
// Checks licenses for commercial solver, and checks shared library loading
// for or-tools.
static bool GurobiIsCorrectlyInstalled();
// 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.