1 /* Copyright (c) 1997-2021
2 Ewgenij Gawrilow, Michael Joswig, and the polymake team
3 Technische Universität Berlin, Germany
4 https://polymake.org
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version: http://www.gnu.org/licenses/gpl.txt.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 --------------------------------------------------------------------------------
16 */
17
18 #pragma once
19
20 #include "polymake/Matrix.h"
21 #include "polymake/Vector.h"
22 #include "polymake/Set.h"
23 #include "polymake/polytope/solve_LP.h"
24 #if POLYMAKE_DEBUG
25 # include "polymake/vector"
26 # include "polymake/client.h"
27 #endif
28
29 #define TO_DISABLE_OUTPUT
30 #include "polymake/common/TOmath_decl.h"
31 #include "TOSimplex/TOSimplex.h"
32
33 namespace polymake { namespace polytope { namespace to_interface {
34
35 template <typename Coord>
36 class Solver : public LP_Solver<Coord> {
37 public:
Solver()38 Solver()
39 #if POLYMAKE_DEBUG
40 : debug_print(get_debug_level() > 1)
41 #endif
42 {}
43
44 // CAUTION: to interpret the return value notice that this is a *dual* simplex
45 LP_Solution<Coord>
46 solve(const Matrix<Coord>& Inequalities, const Matrix<Coord>& Equations,
47 const Vector<Coord>& Objective, bool maximize, const Set<Int>& initial_basis) const;
48
49 LP_Solution<Coord>
solve(const Matrix<Coord> & Inequalities,const Matrix<Coord> & Equations,const Vector<Coord> & Objective,bool maximize,bool)50 solve(const Matrix<Coord>& Inequalities, const Matrix<Coord>& Equations,
51 const Vector<Coord>& Objective, bool maximize, bool) const override
52 {
53 return Solver::solve(Inequalities, Equations, Objective, maximize, Set<Int>());
54 }
55
56 #if POLYMAKE_DEBUG
57 private:
58 const bool debug_print;
59 #endif
60 };
61
62 namespace {
63
64 template <typename Coord>
to_dual_fill_data(const Matrix<Coord> & A1,const Matrix<Coord> & A2,const std::vector<Coord> & MinObjective,std::vector<Coord> & to_coefficients,std::vector<Int> & to_colinds,std::vector<Int> & to_rowbegininds,std::vector<TOSimplex::TORationalInf<Coord>> & to_rowlowerbounds,std::vector<TOSimplex::TORationalInf<Coord>> & to_rowupperbounds,std::vector<TOSimplex::TORationalInf<Coord>> & to_varlowerbounds,std::vector<TOSimplex::TORationalInf<Coord>> & to_varupperbounds,std::vector<Coord> & to_objective)65 void to_dual_fill_data(const Matrix<Coord>& A1, const Matrix<Coord>& A2, const std::vector<Coord>& MinObjective,
66 std::vector<Coord>& to_coefficients, std::vector<Int>& to_colinds, std::vector<Int>& to_rowbegininds,
67 std::vector<TOSimplex::TORationalInf<Coord> >& to_rowlowerbounds, std::vector<TOSimplex::TORationalInf<Coord> >& to_rowupperbounds,
68 std::vector<TOSimplex::TORationalInf<Coord> >& to_varlowerbounds, std::vector<TOSimplex::TORationalInf<Coord> >& to_varupperbounds,
69 std::vector<Coord>& to_objective)
70 {
71 Int nnz = 0;
72 for (Int i = 0; i < A1.rows(); ++i) {
73 for (Int j = 1; j < A1.cols(); ++j) {
74 if (!is_zero(A1(i,j))){
75 ++nnz;
76 }
77 }
78 }
79 for (Int i = 0; i < A2.rows(); ++i) {
80 for (Int j = 1; j < A2.cols(); ++j) {
81 if (!is_zero(A2(i,j))){
82 ++nnz;
83 }
84 }
85 }
86 to_coefficients.reserve(nnz);
87 to_colinds.reserve(nnz);
88
89 /*
90 note: polymake input with rows [c_i, a_i] and [c_j, a_j] with a_i=-a_j
91 could be reduced to one column with 'to_rowupperbounds' and 'to_rowlowerbounds'.
92 */
93
94 const Int m = A1.cols()-1;
95 const Int n1 = A1.rows();
96 const Int n2 = A2.rows();
97 const Int n = n1+n2;
98
99 assert(A2.cols() == 0 || m == A2.cols()-1);
100
101 to_rowbegininds.reserve(m+1);
102
103 for (Int i = 1; i <= m; ++i) {
104 to_rowbegininds.push_back(to_coefficients.size());
105 for (Int j = 0; j < n1; ++j) {
106 if (!is_zero(A1(j, i))) {
107 to_coefficients.push_back(A1(j, i));
108 to_colinds.push_back(j);
109 }
110 }
111 for (Int j = 0; j < n2; ++j) {
112 if (!is_zero(A2(j, i))) {
113 to_coefficients.push_back(A2(j, i));
114 to_colinds.push_back(n1+j);
115 }
116 }
117 }
118 to_rowbegininds.push_back(to_coefficients.size());
119
120 to_rowupperbounds.reserve(m);
121 for (Int i = 0; i < m; ++i) {
122 to_rowupperbounds.push_back(MinObjective[i]); // A'u + E'v =< c
123 }
124 to_rowlowerbounds = to_rowupperbounds; // A'u + E'v >= c
125
126 to_objective.reserve(n);
127 to_varlowerbounds.reserve(n);
128 to_varupperbounds.reserve(n);
129 for (Int i = 0; i < n1; ++i) {
130 to_objective.push_back(A1(i, 0)); // = b_i
131 to_varlowerbounds.push_back(Coord(0)); // u_i >= 0
132 to_varupperbounds.push_back(true); // true = no restriction
133 }
134 for (Int i = 0; i < n2; ++i) {
135 to_objective.push_back(A2(i,0)); // = d_i
136 to_varlowerbounds.push_back(true);
137 to_varupperbounds.push_back(true);
138 }
139 }
140
141 } // end anonymous namespace
142
143
144
145 /* This method uses the TOSimplex-LP-solver to solve the LP.
146 * TOSimplex is a dual LP solver, so it cannot decide whether a given LP is infeasible or unbounded.
147 * To avoid this problem, the LP is implicitly dualized:
148 *
149 * min c'x - min -b'u -d'v
150 * s.t. Ax >= b <==> s.t. A'u + E'v = c
151 * Ex == d u >= 0
152 *
153 * After solving, the dual variables are obtained, if the problem is feasible.
154 * note: restrictions on variables (in this case u >= 0) are handeld
155 * via 'to_varupperbounds' and 'to_varlowerbounds'.
156 */
157
158 template <typename Coord>
159 LP_Solution<Coord>
solve(const Matrix<Coord> & Inequalities,const Matrix<Coord> & Equations,const Vector<Coord> & Objective,bool maximize,const Set<Int> & initial_basis)160 Solver<Coord>::solve(const Matrix<Coord>& Inequalities, const Matrix<Coord>& Equations,
161 const Vector<Coord>& Objective, bool maximize, const Set<Int>& initial_basis) const
162 {
163 using to_solver = TOSimplex::TOSolver<Coord,Int>;
164
165 const Int n = Inequalities.cols()-1; // beware of leading constant term
166
167 #if POLYMAKE_DEBUG
168 if (debug_print) {
169 const Int m = Inequalities.rows() + Equations.rows();
170 cout << "to_solve_lp(m=" << m << " n=" << n << ")" << endl;
171 }
172 #endif
173
174 // translate inequality constraints into sparse description
175 std::vector<Coord> to_coefficients;
176 std::vector<Int> to_colinds, to_rowbegininds;
177 std::vector<TOSimplex::TORationalInf<Coord>> to_rowlowerbounds, to_rowupperbounds, to_varlowerbounds, to_varupperbounds;
178 std::vector<Coord> to_objective;
179
180 // translate objective function into dense description, take direction into account
181 std::vector<Coord> to_obj(n);
182 if (maximize) {
183 for (Int j = 1; j <= n; ++j)
184 to_obj[j-1] = -Objective[j];
185 } else {
186 for (Int j = 1; j <= n; ++j)
187 to_obj[j-1] = Objective[j];
188 }
189
190 // Fill the vectors with the dual problem
191 to_dual_fill_data(Inequalities, Equations, to_obj, to_coefficients, to_colinds, to_rowbegininds, to_rowlowerbounds, to_rowupperbounds, to_varlowerbounds, to_varupperbounds, to_objective );
192
193 #if POLYMAKE_DEBUG
194 if (debug_print)
195 cout << "to_coefficients:" << to_coefficients <<"\n"
196 "to_colinds:" << to_colinds << "\n"
197 "to_rowbegininds:" << to_rowbegininds << "\n"
198 "to_obj:" << to_obj << "\n"
199 "to_objective:" << to_objective << endl;
200 #endif
201
202 to_solver solver(to_coefficients, to_colinds, to_rowbegininds, to_objective, to_rowlowerbounds, to_rowupperbounds, to_varlowerbounds, to_varupperbounds);
203
204
205 // add start basis:
206 if (!initial_basis.empty()) {
207 const Int m = Inequalities.rows() + Equations.rows();
208 std::vector<Int> b1(m);
209 std::vector<Int> b2(n); //slack variables
210 /*
211 interpretation of the entries
212 0 : the value is between the bounds (non strict)
213 1 : the lower bound is attained
214 2 : the upper bound is attained
215 */
216
217 Int count = Inequalities.cols()-1;
218 for (const Int i : initial_basis) {
219 b1[i] = 1;
220 if (--count <= 0) break;
221 }
222 solver.setBase(b1, b2);
223 }
224
225 LP_Solution<Coord> result;
226 switch (solver.opt()) {
227 case 0: {
228 // solved to optimality
229 result.status = LP_status::valid;
230 result.objective_value = solver.getObj();
231 if (!maximize) negate(result.objective_value);
232 const std::vector<Coord>& tox(solver.getY());
233 result.solution.resize(1+n);
234 result.solution[0] = one_value<Coord>();
235 for (Int j = 1; j <= n; ++j)
236 result.solution[j] = -tox[j-1]; // It seems that the duals have an unexpected sign, so let's negate them
237 break;
238 }
239 case 1:
240 result.status = LP_status::unbounded;
241 break;
242 default:
243 result.status = LP_status::infeasible;
244 break;
245 }
246
247 return result;
248 }
249
250 } } }
251
252
253 // Local Variables:
254 // mode:C++
255 // c-basic-offset:3
256 // indent-tabs-mode:nil
257 // End:
258