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