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 /** @file Double_Description.h
19     @brief Implementation of files for tropical double description
20   */
21 
22 #pragma once
23 
24 #include "polymake/Rational.h"
25 #include "polymake/TropicalNumber.h"
26 #include "polymake/Array.h"
27 #include "polymake/Matrix.h"
28 #include "polymake/ListMatrix.h"
29 #include "polymake/Vector.h"
30 #include "polymake/Set.h"
31 #include "polymake/tropical/covectors.h"
32 #include "polymake/tropical/thomog.h"
33 
34 namespace polymake {
35 namespace tropical {
36 
37 /*
38  *  @brief: check if point is contained in a tropical cone defined by
39  *  inequalities which are given by their apices and infeasible sectors
40  */
41 template <typename VectorTop, typename MatrixTop, typename Addition, typename Scalar>
is_contained(const GenericVector<VectorTop,TropicalNumber<Addition,Scalar>> & point,const GenericMatrix<MatrixTop,TropicalNumber<Addition,Scalar>> & apices,const Array<Set<Int>> & sectors)42 bool is_contained(const GenericVector<VectorTop, TropicalNumber<Addition, Scalar>>& point,
43                   const GenericMatrix<MatrixTop, TropicalNumber<Addition, Scalar>>& apices,
44                   const Array<Set<Int>>& sectors)
45 {
46    Int row_index = 0;
47    const IncidenceMatrix<> M(generalized_apex_covector(point, apices));
48    for (const auto& r : rows(M)) {
49       if (incl(r, sectors[row_index]) <= 0)
50          return false;
51       ++row_index;
52    }
53    return true;
54 }
55 
56 /*
57  *  @brief: check if a point fulfills the inequality system A x <= B x for min resp. A x >= B x for max
58  */
59 template <typename VectorTop, typename Matrix1, typename Matrix2, typename Addition, typename Scalar>
is_contained(const GenericVector<VectorTop,TropicalNumber<Addition,Scalar>> & point,const GenericMatrix<Matrix1,TropicalNumber<Addition,Scalar>> & lhs,const GenericMatrix<Matrix2,TropicalNumber<Addition,Scalar>> & rhs)60 bool is_contained(const GenericVector<VectorTop, TropicalNumber<Addition, Scalar>>& point,
61                   const GenericMatrix<Matrix1, TropicalNumber<Addition, Scalar>>& lhs,
62                   const GenericMatrix<Matrix2, TropicalNumber<Addition, Scalar>>& rhs)
63 {
64    for (Int i = 0; i < lhs.rows(); ++i) {
65       if (point * lhs > point * rhs) return false;
66    }
67    return true;
68 }
69 
70 /*
71  *  @brief: convert inequality A x <= B x for min resp. A x >= B x for max
72  *  to the description by
73  *  apices and INfeasible sectors. Here, B is on the INfeasible side.
74  */
75 template <typename Addition, typename Scalar>
76 std::pair<Matrix<TropicalNumber<Addition, Scalar>>, Array<Set<Int>>>
matrixPair2apexSet(const Matrix<TropicalNumber<Addition,Scalar>> & A,const Matrix<TropicalNumber<Addition,Scalar>> & B)77 matrixPair2apexSet(const Matrix<TropicalNumber<Addition, Scalar>>& A,
78                    const Matrix<TropicalNumber<Addition,Scalar>>& B)
79 {
80    using TNumber = TropicalNumber<Addition, Scalar>;
81    Array<Set<Int>> sectors(A.rows());
82    Matrix<TNumber> W(A.rows(), A.cols());
83    TNumber entry;
84    for (Int i = 0; i < A.rows(); ++i) {
85       for (Int j = 0; j < A.cols(); ++j) {
86          if (A(i,j) != B(i,j)) {
87             entry = A(i,j) + B(i,j);
88             W(i,j) = entry;
89             if (entry == B(i,j)) sectors[i] += j;
90          }
91       }
92    }
93    return { W, sectors };
94 }
95 
96 /*
97  *  @brief: convert inequality A x <= B x for min resp. A x >= B x for max
98  *  to the description by
99  *  apices and INfeasible sectors. Here, B is on the INfeasible side.
100  */
101 template <typename Addition, typename Scalar>
102 std::pair<Matrix<TropicalNumber<Addition, Scalar>>, Array<Int>>
matrixPair2splitApices(const Matrix<TropicalNumber<Addition,Scalar>> & A,const Matrix<TropicalNumber<Addition,Scalar>> & B)103 matrixPair2splitApices(const Matrix<TropicalNumber<Addition, Scalar>>& A,
104                        const Matrix<TropicalNumber<Addition, Scalar>>& B)
105 {
106    const Int n = A.rows(), d = A.cols();
107    if (n != B.rows())
108       throw std::runtime_error("dimension mismatch for inequality system: different number of rows");
109    if (d != B.cols())
110       throw std::runtime_error("dimension mismatch for inequality system: different number of columns");
111 
112    using TNumber = TropicalNumber<Addition,Scalar>;
113    std::list<Int> negative_indices;
114    ListMatrix<Vector<TNumber>> W(0, A.cols());
115    for (Int i = 0; i < n; ++i) {
116       for (Int j = 0; j < d; ++j) {
117          if (A(i,j) != A(i,j) + B(i,j)) {
118             Vector<TNumber> modified_row = A.row(i);
119             modified_row[j] = B(i,j);
120             negative_indices.push_back(j);
121             W /= modified_row;
122          }
123       }
124    }
125    return { Matrix<TNumber>(W), Array<Int>(W.rows(), negative_indices.begin()) };
126 }
127 
128 
129 /*
130  *  @brief: reduce a set of generators of a tropical cone to the
131  *  extremal generators
132  *
133  */
134 template <typename MatrixTop, typename Addition, typename Scalar>
135 Matrix<TropicalNumber<Addition, Scalar>>
extremals_from_generators(const GenericMatrix<MatrixTop,TropicalNumber<Addition,Scalar>> & generators)136 extremals_from_generators(const GenericMatrix<MatrixTop, TropicalNumber<Addition, Scalar>>& generators)
137 {
138    using TNumber = TropicalNumber<Addition,Scalar>;
139    const Int n = generators.rows(), dim = generators.cols();
140 
141    ListMatrix<Vector<TNumber>> extremals;
142 
143    // removing double points
144    Set<Int> reduced_generators;
145    for (Int i = 0; i < n; ++i) {
146       bool redundant = false;
147       for (auto r = entire(rows(generators.minor(reduced_generators,All))); !r.at_end(); ++r) {
148          if (dim == single_covector(generators[i],(*r)).size()) {
149             redundant = true;
150             break;
151          }
152       }
153       if (!redundant)
154          reduced_generators += i;
155    }
156 
157    for (auto r = entire(rows(generators.minor(reduced_generators,All))); !r.at_end(); ++r) {
158       bool is_extremal = false;
159 
160       // checking covector criterion for extremality, using exposedness
161       for (auto coord = entire(rows(single_covector((*r), generators.minor(reduced_generators,All)))); !coord.at_end(); ++coord) {
162          if (coord->size() == 1) {
163             is_extremal = true;
164             break;
165          }
166       }
167       if (is_extremal)
168          extremals /= *r;
169    }
170    return extremals;
171 }
172 
173 
174 /*
175  *  @brief: compute the extremals of a tropical cone given by the
176  *  intersection of a tropical halfspace with another tropical cone
177  *  which is given by its extremals.
178  */
179 template <typename MatrixTop, typename Vector1, typename Vector2, typename Addition, typename Scalar>
180 Matrix<TropicalNumber<Addition, Scalar>>
intersection_extremals(const GenericMatrix<MatrixTop,TropicalNumber<Addition,Scalar>> & generators,const GenericVector<Vector1,TropicalNumber<Addition,Scalar>> & feasible_side,const GenericVector<Vector2,TropicalNumber<Addition,Scalar>> & infeasible_side)181 intersection_extremals(const GenericMatrix<MatrixTop, TropicalNumber<Addition, Scalar>>& generators,
182                        const GenericVector<Vector1, TropicalNumber<Addition, Scalar>>& feasible_side,
183                        const GenericVector<Vector2, TropicalNumber<Addition, Scalar>>& infeasible_side)
184 {
185    using TNumber = TropicalNumber<Addition,Scalar>;
186 
187    Set<Int> remaining_generators;
188    Int r_index = 0;
189 
190    // check for all generators if they are contained in the halfspace given by the pair
191    // (feasible_side, infeasible_side)
192    for (const auto& r : rows(generators)) {
193       if (Addition::orientation()*(feasible_side*r).compare(infeasible_side*r) <= 0)
194          remaining_generators += r_index;
195       ++r_index;
196    }
197    Set<Vector<TNumber>> new_points;
198    for (auto g = entire(rows(generators.minor(remaining_generators,All))); !g.at_end(); ++g) {
199       for (auto h = entire(rows(generators.minor(~remaining_generators,All))); !h.at_end(); ++h) {
200          Vector<TNumber> k = (infeasible_side * (*h)) * (*g) + (feasible_side * (*g)) * (*h);
201          new_points += normalized_first(k);
202       }
203    }
204 
205    Matrix<TNumber> new_generators(new_points.size(), feasible_side.dim(), entire(new_points));
206    new_generators /= generators.minor(remaining_generators, All);
207    return extremals_from_generators(new_generators);
208 }
209 
210 
211 /*
212  * @brief: compute the extremals of a tropical cone given
213  * as the intersection of tropical halfspaces
214  *
215  */
216 template <typename Matrix1, typename Matrix2, typename Addition, typename Scalar>
217 Matrix<TropicalNumber<Addition, Scalar>>
extremals_from_halfspaces(const GenericMatrix<Matrix1,TropicalNumber<Addition,Scalar>> & feasible_side,const GenericMatrix<Matrix2,TropicalNumber<Addition,Scalar>> & infeasible_side)218 extremals_from_halfspaces(const GenericMatrix<Matrix1, TropicalNumber<Addition, Scalar>>& feasible_side,
219                           const GenericMatrix<Matrix2, TropicalNumber<Addition, Scalar>>& infeasible_side)
220 {
221    using TNumber = TropicalNumber<Addition,Scalar>;
222 
223    if (infeasible_side.rows() != feasible_side.rows())
224       throw std::runtime_error("dimension mismatch for inequality system: different number of rows");
225 
226    const Int n_halfspaces = infeasible_side.rows();
227 
228    Matrix<TNumber> extremals(unit_matrix<TNumber>(infeasible_side.cols()));
229 
230    for (Int i = 0; i < n_halfspaces; ++i) {
231       extremals = intersection_extremals(extremals, feasible_side[i], infeasible_side[i]);
232    }
233    return extremals;
234 }
235 
236 
237 /*
238  *  @brief: compute the extremals of a monomial tropical cone given by the
239  *  intersection of a tropical halfspace (defined via nondominated point) with a monomial tropical cone
240  *  which is given by generators and apices.
241  */
242 template <typename Matrix1, typename Matrix2, typename VectorTop, typename Addition, typename Scalar>
243 Matrix<TropicalNumber<Addition, Scalar>>
monoextremals(const GenericMatrix<Matrix1,TropicalNumber<Addition,Scalar>> & generators,const GenericMatrix<Matrix2,TropicalNumber<Addition,Scalar>> & apices,const GenericVector<VectorTop,Scalar> & non_dominated_point)244 monoextremals(const GenericMatrix<Matrix1, TropicalNumber<Addition, Scalar>>& generators,
245               const GenericMatrix<Matrix2, TropicalNumber<Addition, Scalar>>& apices,
246               const GenericVector<VectorTop, Scalar>& non_dominated_point)
247 {
248    using TNumber = TropicalNumber<Addition,Scalar>;
249 
250    Set<Int> remaining_generators;
251    Int r_index = 0;
252    Vector<TNumber> infeasible_side(non_dominated_point.dim()+1);
253    infeasible_side[0] = TNumber::one();
254 
255    Vector<TNumber> feasible_side(non_dominated_point.dim()+1);
256    feasible_side.slice(range_from(1)) = -non_dominated_point;
257 
258    Vector<TNumber> new_apex((0|non_dominated_point));
259 
260    // check for all generators if they are contained in the halfspace given by the pair
261    // (feasible_side, infeasible_side)
262    for (const auto r : rows(generators)) {
263       if (Addition::orientation()*(feasible_side*r).compare(infeasible_side*r) <= 0)
264          remaining_generators += r_index;
265       ++r_index;
266    }
267 
268    Set<Vector<TNumber>> new_generators;
269 
270    for (auto g = entire(rows(generators.minor(remaining_generators, All))); !g.at_end(); ++g) {
271       for (auto h = entire(rows(generators.minor(~remaining_generators,All))); !h.at_end(); ++h) {
272          Vector<TNumber> k = (infeasible_side * (*h)) * (*g) + (feasible_side * (*g)) * (*h);
273 
274          // the next loop should determine if k is redundant; should probably be simplified -- check if pairwise incomparable
275          Set<Int> covered_sectors;
276          Set<Int> apex_sectors;
277          for (auto apex : rows(apices)) {
278             // why does (apices/new_apex) not work??
279             apex_sectors = single_covector(apex, k);
280             if ((apex_sectors.contains(0)) & (apex_sectors.size()==2)) {
281                covered_sectors += apex_sectors;
282             }
283          }
284          // FIXME: this is a specific check of extremality for monomial tropical cones -- maybe here is a mistake
285          apex_sectors = single_covector(new_apex, k);
286          if (apex_sectors.contains(0)) {
287             covered_sectors += apex_sectors;
288          }
289          if (covered_sectors == support(k)) {
290             new_generators += normalized_first(k);
291          }
292       }
293    }
294    Matrix<TNumber> all_generators(new_generators.size(), new_apex.dim(), entire(new_generators));
295 
296    return generators.minor(remaining_generators,All)/all_generators;
297 }
298 
299 
300 /*
301  * @brief: determine the dual generators of dehomogenized monomial generators
302  *
303  * @return: a pair of dual generators and the incidences with the primal generators
304  */
305 template <typename MatrixTop, typename Scalar>
306 std::pair<Matrix<TropicalNumber<Min, Scalar>>, IncidenceMatrix<>>
monomial_dual_description(const GenericMatrix<MatrixTop,Scalar> & monomial_generators)307 monomial_dual_description(const GenericMatrix<MatrixTop, Scalar>& monomial_generators)
308 {
309    using TNumber = TropicalNumber<Min, Scalar>;
310 
311    Int dim = monomial_generators.cols();
312 
313    Matrix<TNumber> gen(unit_matrix<TNumber>(dim+1));
314    ListMatrix<Vector<TNumber>> apices;
315 
316    for (const auto& mg : rows(monomial_generators)) {
317       gen = monoextremals(gen, apices, mg);
318       const auto new_apex = TNumber::one() | convert_to<TNumber>(mg);
319       apices /= new_apex;
320    }
321 
322    ListMatrix<Vector<TNumber>> finite_gen;
323 
324    // select only those generators with first coord 0
325    for (const auto& g : rows(gen)) {
326       if (g[0]==0) finite_gen /= g;
327    }
328 
329    // compute the incidences of primal and dual generators
330    IncidenceMatrix<> vif(monomial_generators.rows(), finite_gen.rows());
331    Int i = 0;
332    for (const auto& mg : rows(monomial_generators)) {
333       Int j = 0;
334       for (const auto& dg : rows(finite_gen)) {
335          Vector<Scalar> sg(dg.slice(range_from(1)));
336          if (accumulate(sg-mg, operations::min()) == static_cast<const Scalar&>(dg[0]))
337             vif(i, j) = true;
338          ++j;
339       }
340       ++i;
341    }
342 
343    return { Matrix<TNumber>(finite_gen), vif };
344 }
345 
346 } }
347 
348 
349 // Local Variables:
350 // mode:C++
351 // c-basic-offset:3
352 // indent-tabs-mode:nil
353 // End:
354