1 /*
2 This program is free software; you can redistribute it and/or
3 modify it under the terms of the GNU General Public License
4 as published by the Free Software Foundation; either version 2
5 of the License, or (at your option) any later version.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor,
15 Boston, MA 02110-1301, USA.
16
17 ---
18 Copyright (C) 2011 - 2015, Simon Hampe <simon.hampe@googlemail.com>
19
20 ---
21 Copyright (c) 2016-2021
22 Ewgenij Gawrilow, Michael Joswig, and the polymake team
23 Technische Universität Berlin, Germany
24 https://polymake.org
25
26 Implements lines_in_cubic_helper.h
27 */
28
29 #include "polymake/client.h"
30 #include "polymake/Rational.h"
31 #include "polymake/Matrix.h"
32 #include "polymake/Vector.h"
33 #include "polymake/linalg.h"
34 #include "polymake/IncidenceMatrix.h"
35 #include "polymake/tropical/codim_one_with_locality.h"
36 #include "polymake/tropical/divisor.h"
37 #include "polymake/tropical/linear_algebra_tools.h"
38 #include "polymake/tropical/lines_in_cubic_reachable.h"
39 #include "polymake/tropical/lines_in_cubic_helper.h"
40 #include "polymake/tropical/lines_in_cubic_data.h"
41 #include "polymake/polytope/convex_hull.h"
42
43
44 namespace polymake { namespace tropical {
45
46 /**
47 @brief Takes a fan_intersection_result and cleans up the result so that no cone is contained in another and that cones are sorted according to their dimension.
48 @param fan_intersection_result fir
49 @return DirectionIntersection
50 */
cleanUpIntersection(const fan_intersection_result & fir)51 DirectionIntersection cleanUpIntersection(const fan_intersection_result& fir)
52 {
53 DirectionIntersection result;
54 result.rays = fir.rays;
55 IncidenceMatrix<> fir_cones = fir.cones;
56 // First we sort all cells according to their dimension
57 Set<Int> cell_set;
58 Set<Int> edge_set;
59 Set<Int> point_set;
60 for (Int fc = 0; fc < fir_cones.rows(); ++fc) {
61 if (fir_cones.row(fc).size() > 2) cell_set += fc;
62 if (fir_cones.row(fc).size() == 2) edge_set += fc;
63 if (fir_cones.row(fc).size() == 1) point_set += fc;
64 }
65 // Go through all edges, compare to cells, remove redundant ones
66 Set<Int> redundant_edges;
67 for (auto e = entire(edge_set); !e.at_end(); ++e) {
68 bool found_container = false;
69 for (auto c = entire(cell_set); !c.at_end(); ++c) {
70 if ( (fir_cones.row(*c) * fir_cones.row(*e)).size() == fir_cones.row(*e).size()) {
71 found_container = true;
72 break;
73 }
74 }
75 if (found_container) redundant_edges += (*e);
76 }
77 edge_set -= redundant_edges;
78
79 // Same for points
80 Set<Int> redundant_points;
81 for (auto p = entire(point_set); !p.at_end(); ++p) {
82 bool found_container = false;
83 Set<Int> containers = cell_set + edge_set;
84 for (auto c = entire(containers); !c.at_end(); ++c) {
85 if ( (fir_cones.row(*c) * fir_cones.row(*p)).size() == fir_cones.row(*p).size()) {
86 found_container = true;
87 break;
88 }
89 }
90 if (found_container) redundant_points += (*p);
91 }
92 point_set -= redundant_points;
93
94 result.cells = fir_cones.minor(cell_set,All);
95 result.edges = fir_cones.minor(edge_set,All);
96 result.points = fir_cones.minor(point_set,All);
97
98 return result;
99 } // END cleanUpIntersection
100
101 /**
102 @brief This takes a (two-dimensional) cone in R^3 in terms of a subset of rays and computes all codimension one faces.
103 @return A FacetData object (see above)
104 */
computeFacets(const Matrix<Rational> & rays,const Set<Int> & cone)105 FacetData computeFacets(const Matrix<Rational>& rays, const Set<Int>& cone)
106 {
107 // Compute facet equations and store them
108 const auto ceq = polytope::enumerate_facets(rays.minor(cone, All), false);
109 FacetData result;
110 result.eq = ceq.second.row(0);
111 result.ineqs = ceq.first;
112 // Now go through all inequalities and find the corresponding vertices
113 Set<Int> exclude_ineqs;
114 RestrictedIncidenceMatrix<> facets;
115 for (auto ineq=entire<indexed>(rows(result.ineqs)); !ineq.at_end(); ++ineq) {
116 Set<Int> facet;
117 bool vertex_seen=false;
118 for (auto c=entire(cone); !c.at_end(); ++c) {
119 if (is_zero((*ineq) * rays.row(*c))) {
120 facet.push_back(*c);
121 if (!vertex_seen && !is_zero(rays(*c, 0))) {
122 vertex_seen=true;
123 }
124 }
125 }
126 // Each facet has to have at least one vertex
127 if (vertex_seen) {
128 facets /= facet;
129 } else {
130 exclude_ineqs += ineq.index();
131 }
132 }
133 result.facets = std::move(facets);
134 if (!exclude_ineqs.empty()) {
135 result.ineqs = result.ineqs.minor(~exclude_ineqs, All);
136 }
137 return result;
138 }
139
140 // ------------------------------------------------------------------------------------------------
141
142 /**
143 @brief This takes a vertex in the cubic (whose function's domain is describes by frays and fcones) and a direction and computes the vertex w farthest away from vertex in this direction, such that the convex hull of vertex and w still lies in X. It returns the empty vertex, if the complete half-line lies in X.
144 */
maximalDistanceVector(const Vector<Rational> & vertex,const Vector<Rational> & direction,const Matrix<Rational> & frays,const IncidenceMatrix<> & fcones,const Matrix<Rational> & funmat)145 Vector<Rational> maximalDistanceVector(const Vector<Rational>& vertex,
146 const Vector<Rational>& direction,
147 const Matrix<Rational>& frays,
148 const IncidenceMatrix<>& fcones,
149 const Matrix<Rational>& funmat)
150 {
151 // Create the one-dimensional half-line from vertex
152 Matrix<Rational> hl_rays = vector2row(vertex) / direction;
153 IncidenceMatrix<> hl_cones({ { 0, 1 } });
154 Matrix<Rational> lin(0, hl_rays.cols());
155 //Intersect with f-domain
156 DirectionIntersection ref_line =
157 cleanUpIntersection(fan_intersection(hl_rays, lin, hl_cones, frays,lin,fcones));
158 // Find vertex
159 Int v_index = -1;
160 for (Int r = 0; r < ref_line.rays.rows(); ++r) {
161 if (ref_line.rays.row(r) == vertex) {
162 v_index = r;
163 break;
164 }
165 }
166 IncidenceMatrix<> rays_in_cones = T(ref_line.edges);
167 // Go through edges, starting at vertex and check if it is contained in X
168 Int current_edge = *(rays_in_cones.row(v_index).begin());
169 Int current_vertex = v_index;
170 // When the current edge has only one vertex, we're done
171 do {
172 // Check if the edge lies in X
173 Vector<Rational> interior_point =
174 accumulate(rows(ref_line.rays.minor(ref_line.edges.row(current_edge),All)), operations::add()) /
175 accumulate(ref_line.rays.minor(ref_line.edges.row(current_edge),All).col(0),operations::add());
176 if (!maximumAttainedTwice(funmat * interior_point)) {
177 return ref_line.rays.row(current_vertex);
178 } else {
179 current_vertex = *( (ref_line.edges.row(current_edge) - current_vertex).begin());
180 if (ref_line.rays.row(current_vertex)[0] == 0) {
181 current_vertex = -1;
182 } else {
183 current_edge = *( (rays_in_cones.row(current_vertex) - current_edge).begin());
184 }
185 }
186 } while(current_vertex >= 0);
187
188 return Vector<Rational>();
189 }
190
191 // ------------------------------------------------------------------------------------------------
192
193 /**
194 @brief Takes two vectors v1 and v2 and a standard direction e_i + e_j for i,j in {0,..,3} and computes the rational number r, such that v1 + r*direction = v2. Returns also zero, if no such number exists. It also accepts a 0-dimensional vertex for v2 and will return 0 in that case.
195 */
vertexDistance(const Vector<Rational> & v1,const Vector<Rational> & v2,const Vector<Rational> & direction)196 Rational vertexDistance(const Vector<Rational>& v1, const Vector<Rational>& v2, const Vector<Rational>& direction)
197 {
198 if (v2.dim() == 0) return 0;
199 Vector<Rational> diff = v2 - v1;
200 Rational div = 0;
201 for (Int i = 1; i <= 3; ++i) {
202 if ((diff[i] == 0 && direction[i] != 0) || (diff[i] != 0 && direction[i] == 0))
203 return 0;
204 if (diff[i] != 0) {
205 Rational d = diff[i] / direction[i];
206 if (div == 0) {
207 div = d;
208 } else {
209 if (d != div) return 0;
210 }
211 }
212 }
213 return div;
214 }
215
216 // ------------------------------------------------------------------------------------------------
217
218 /**
219 @brief Takes a vertex family and computes the index of the standard direction in 0,..,3 corresponding to its edge
220 */
vertexFamilyDirection(const VertexFamily & f)221 Int vertexFamilyDirection(const VertexFamily& f)
222 {
223 Vector<Rational> dir;
224 if (f.edge(0,0) == 0) dir = f.edge.row(0);
225 if (f.edge(1,0) == 0) dir = f.edge.row(1);
226 if (dir.dim() == 0) dir = f.edge.row(0) - f.edge.row(1);
227 if (dir[1] == 0 && dir[2] == 0) return 3;
228 if (dir[1] == 0 && dir[3] == 0) return 2;
229 if (dir[2] == 0 && dir[3] == 0) return 1;
230 return 0;
231 }
232
233 // ------------------------------------------------------------------------------------------------
234
235 /**
236 @brief Computes all edge families lying in a 2-dimensional cone for a given direction
237 @param DirectionIntersection cone A cone, refined along f
238 @param Matrix<Rational> z_border The intersection of the cone with a cone in the 0-i-rechable locus
239 @param Matrix<Rational> c_border The intersection of the cone with a cone in the j-k-reachable locus
240 @param Int leafAtZero The index of the leaf together with 0
241 @param Matrix<Rational> funmat The function matrix of f, made compatible for vector multiplication
242 @return LinesInCellResult A list of all edge families and edge lines lying in the cone
243 */
computeEdgeFamilies(const DirectionIntersection & cone,const Matrix<Rational> & z_border,const Matrix<Rational> & c_border,Int leafAtZero,const Matrix<Rational> & funmat)244 LinesInCellResult computeEdgeFamilies(const DirectionIntersection& cone,
245 const Matrix<Rational>& z_border,
246 const Matrix<Rational>& c_border,
247 Int leafAtZero,
248 const Matrix<Rational>& funmat)
249 {
250 Matrix<Rational> degree = -unit_matrix<Rational>(3);
251 degree = ones_vector<Rational>(3) / degree;
252 degree = zero_vector<Rational>(4) | degree;
253
254 // First we project all vertices of the cone onto z_border
255 Matrix<Rational> z_edge_rays = z_border;
256 RestrictedIncidenceMatrix<> z_edges_grow={ {0, 1} };
257 Vector<Rational> direction = degree.row(0) + degree.row(leafAtZero);
258 Vector<Int> rem(sequence(1,3) - leafAtZero);
259
260 for (Int dr = 0; dr < cone.rays.rows(); ++dr) {
261 if (cone.rays(dr,0) == 1) {
262 // We go through all edges of z_edges until we find one that intersects (vertex + R_>=0 *direction)
263 // This is computed as follows: Assume p is a vertex of an edge of z_edges and that w is the direction
264 // from p into that edge (either a ray or p2-p1, if the edge is bounded). Then we compute a linear representation of (vertex - p_1) in terms of w and
265 // direction. If a representation exists and the second edge generator is also a vertex, we have to check
266 // that the coefficient of w = p2-p1 is in between 0 and 1, otherwise it has to be > 0
267 //
268 // If we find an intersecting edge, we refine it (in z_edges) such that it contains the intersection point.
269 for (Int ze = 0; ze < z_edges_grow.rows(); ++ze) {
270 Matrix<Rational> edge_generators = z_border.minor(z_edges_grow.row(ze), All);
271 Vector<Rational> p1 = edge_generators(0,0) == 0? edge_generators.row(1) : edge_generators.row(0);
272 bool bounded = edge_generators(0,0) == edge_generators(1,0);
273 Vector<Rational> w;
274 if (bounded) {
275 w = edge_generators.row(1) - edge_generators.row(0);
276 } else {
277 w = edge_generators(0,0) == 0 ? edge_generators.row(0) : edge_generators.row(1);
278 }
279 Vector<Rational> lin_rep = linearRepresentation(cone.rays.row(dr) - p1, vector2row(w) / direction);
280 // Check that:
281 // - There is a representation
282 // - The coefficient of w is > 0 (and < 1 if bounded)
283 if (lin_rep.dim() > 0) {
284 if (lin_rep[0] > 0 && (!bounded || lin_rep[0] < 1)) {
285 // Then we add a vertex, stop searching for an edge and go to the next vertex
286 Vector<Rational> new_vertex = p1 + lin_rep[0]*w;
287 z_edge_rays /= new_vertex;
288 auto edge_index_list=z_edges_grow.row(ze).begin();
289 const Set<Int> one_cone={ *edge_index_list, z_edge_rays.rows()-1 };
290 const Set<Int> other_cone={ *++edge_index_list, z_edge_rays.rows()-1 };
291 z_edges_grow.row(ze)=one_cone;
292 z_edges_grow /= other_cone;
293 break;
294 }
295 } // END if linear rep exists
296 } // END iterate z_edges
297 } // END if vertex
298 } // END project vertices
299
300 const IncidenceMatrix<> z_edges(std::move(z_edges_grow));
301 // Then refine the cone along the new z_edges - direction
302 // and compute codim one data
303 Matrix<Rational> dummy_lineality(0, z_border.cols());
304 DirectionIntersection refined_cone =
305 cleanUpIntersection(fan_intersection(cone.rays, dummy_lineality, cone.cells,
306 z_edge_rays, dummy_lineality / direction, z_edges));
307 CodimensionOneResult codimData =
308 calculateCodimOneData(refined_cone.rays, refined_cone.cells, dummy_lineality, IncidenceMatrix<>());
309 IncidenceMatrix<> codim =codimData.codimOneCones;
310 IncidenceMatrix<> rayInCo = T(codim);
311 IncidenceMatrix<> coInMax = codimData.codimOneInMaximal;
312 IncidenceMatrix<> maxInCo = T(coInMax);
313
314 // Find all edges that span direction
315 Set<Int> direction_edges;
316 for (Int cc = 0; cc < codim.rows(); ++cc) {
317 Matrix<Rational> cc_rays = refined_cone.rays.minor(codim.row(cc),All);
318 Vector<Rational> cc_span;
319 if (cc_rays(0,0) == cc_rays(1,0)) {
320 cc_span = cc_rays.row(0) - cc_rays.row(1);
321 } else {
322 cc_span = cc_rays(0,0) == 0 ? cc_rays.row(0) : cc_rays.row(1);
323 }
324 if (cc_span[leafAtZero] == 0 && cc_span[rem[0]] == cc_span[rem[1]])
325 direction_edges += cc;
326 }
327 Set<Int> remaining_codim = sequence(0,codim.rows()) - direction_edges;
328
329 // Now we go through all z_edges, find the corresponding codim cone in the refined cone
330 // and check if the complete "path" to the other side lies in X. In addition we keep track of all
331 // vertices of z_edges that do not lie in a 2-dim path to the other side and have to be checked
332 // for isolated solutions
333 Vector<EdgeFamily> family_list(0);
334 Vector<EdgeLine> line_list(0);
335 Vector<VertexLine> vertex_list(0);
336
337 Set<Int> all_vertices_this_side;
338 Set<Int> covered_vertices;
339
340 for (Int ze = 0; ze < z_edges.rows(); ++ze) {
341 Matrix<Rational> ze_rays = z_edge_rays.minor(z_edges.row(ze), All);
342 // Find rays in refined cone
343 Int index_1 = -1;
344 Int index_2 = -1;
345 for (Int rc = 0; rc < refined_cone.rays.rows(); ++rc) {
346 if (refined_cone.rays.row(rc) == ze_rays.row(0)) index_1 = rc;
347 if (refined_cone.rays.row(rc) == ze_rays.row(1)) index_2 = rc;
348 if (index_1 >= 0 && index_2 >= 0) break;
349 }
350 if (refined_cone.rays(index_1,0) == 1) all_vertices_this_side += index_1;
351 if (refined_cone.rays(index_2,0) == 1) all_vertices_this_side += index_2;
352
353 // Now find the codimension one cone containing these two rays
354 Int co_index = -1;
355 for (auto cc = entire(remaining_codim); !cc.at_end(); ++cc) {
356 if (codim.row(*cc).contains(index_1) && codim.row(*cc).contains(index_2)) {
357 co_index = *cc;
358 break;
359 }
360 }
361 remaining_codim -= co_index;
362
363 // Find all maximal cones on the "path" to the other side and check if they are in X
364 Int current_cone = *(coInMax.row(co_index).begin());
365 bool found_bad = false;
366 Int cone_index_other_side = -1;
367 while (current_cone >= 0) {
368 // Compute interior point and check if it is in X
369 Vector<Rational> interior_point =
370 accumulate( rows(refined_cone.rays.minor(refined_cone.cells.row(current_cone),All)), operations::add()) /
371 accumulate(refined_cone.rays.minor(refined_cone.cells.row(current_cone),All).col(0),operations::add());
372 if (!maximumAttainedTwice(funmat * interior_point)) {
373 found_bad = true;
374 break;
375 }
376
377 // If all is fine, find the next maximal cone, i.e. find an unused codimension one face
378 // in that cone and take the other maximal cone adjacent to it.
379 Int next_codim = *( (maxInCo.row(current_cone) * remaining_codim).begin() );
380 remaining_codim -= next_codim;
381
382 Set<Int> next_max = coInMax.row(next_codim) - current_cone;
383 // If there is no other maximal cone, we have arrived at the other end
384 if (next_max.size() == 0) {
385 cone_index_other_side = next_codim;
386 current_cone = -1;
387 } else {
388 current_cone = *(next_max.begin());
389 }
390 } //END iterate maximal cones to the other side
391
392 // If everything lies in X, add the family (and register the vertices as "covered")
393 if (!found_bad) {
394 EdgeFamily ef;
395 ef.edgesAtZero = Vector< Matrix<Rational> >(0);
396 ef.edgesAwayZero = Vector< Matrix<Rational> >(0);
397 ef.leafAtZero = leafAtZero;
398 ef.edgesAtZero |= ze_rays;
399 ef.edgesAwayZero |= refined_cone.rays.minor(codim.row(cone_index_other_side),All);
400 ef.borderAtZero = ef.edgesAtZero[0];
401 ef.borderAwayZero = ef.edgesAwayZero[0];
402 family_list |= ef;
403
404 covered_vertices += codim.row(co_index);
405 }
406
407 } //END iterate z_edges
408
409 // Finally we consider all vertices not in a 2-dim family and check if the line to the other
410 // side is contained in X
411 Set<Int> vertices_to_check = all_vertices_this_side - covered_vertices;
412 Set<Int> used_up_edges;
413 Set<Int> used_up_vertices;
414 for (auto vtc = entire(vertices_to_check); !vtc.at_end(); ++vtc) {
415 used_up_vertices += *vtc;
416
417 // Find first edge containing it
418 Set<Int> current_edge_set = rayInCo.row(*vtc) * direction_edges;
419
420 // If there is no edge, we have a vertex line
421 if (current_edge_set.empty()) {
422 VertexLine vl;
423 vl.vertex = refined_cone.rays.row(*vtc);
424 vl.cells = Set<Int>();
425 vertex_list |= vl;
426 continue;
427 }
428
429 Int current_edge = *(current_edge_set.begin());
430
431 used_up_edges += current_edge;
432 Int vertex_index_other_side;
433 bool found_bad = true;
434 while (current_edge >= 0) {
435 // Compute interior point and check if it is in X
436 Vector<Rational> interior_point =
437 accumulate( rows(refined_cone.rays.minor(codim.row(current_edge),All)), operations::add()) /
438 accumulate(refined_cone.rays.minor(codim.row(current_edge),All).col(0),operations::add());
439 if (!maximumAttainedTwice(funmat * interior_point)) {
440 found_bad = true;
441 break;
442 }
443
444 // Find next edge. If there is none, we have arrived at the other side
445 Int next_vertex = *( (codim.row(current_edge) - used_up_vertices).begin());
446 used_up_vertices += next_vertex;
447 Set<Int> next_edge_set = (rayInCo.row(next_vertex)*direction_edges) - used_up_edges;
448 if (next_edge_set.empty()) {
449 vertex_index_other_side = next_vertex;
450 current_edge = -1;
451 } else {
452 current_edge = *(next_edge_set.begin());
453 used_up_edges += current_edge;
454 }
455 } // END iterate edges to other side
456 if (!found_bad) {
457 EdgeLine el;
458 el.leafAtZero = leafAtZero;
459 el.vertexAtZero = refined_cone.rays.row(*vtc);
460 el.vertexAwayZero = refined_cone.rays.row(vertex_index_other_side);
461 line_list |= el;
462 }
463 } //END iterate non-contained vertices
464
465 // Return result
466 LinesInCellResult result;
467 result.edge_families = family_list;
468 result.edge_lines = line_list;
469 result.vertex_lines = vertex_list;
470
471 return result;
472
473 } // END computeEdgeFamilies
474
475 } }
476