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 #include "polymake/client.h"
19 #include "polymake/Set.h"
20 #include "polymake/Rational.h"
21 #include "polymake/Matrix.h"
22 #include "polymake/graph/DoublyConnectedEdgeList.h"
23 #include "polymake/topaz/DomeVolumeVisitor.h"
24 #include "polymake/IncidenceMatrix.h"
25 #include "polymake/Graph.h"
26 #include "polymake/Vector.h"
27 #include "polymake/graph/graph_iterators.h"
28 #include <cmath>
29 
30 
31 namespace polymake { namespace topaz {
32 
33 using DoublyConnectedEdgeList = graph::dcel::DoublyConnectedEdgeList;
34 using HalfEdge = graph::dcel::HalfEdge;
35 using Face = graph::dcel::Face;
36 
37 class PotatoVisitor : public graph::NodeVisitor<> {
38 private:
39    Graph<Directed>& dual_tree;
40 
41    // The triangulation of the surface.
42    DoublyConnectedEdgeList& dcel;
43 
44    // The vertices of the covering triangulation.
45    std::vector<Vector<Rational>> points;
46 
47    // The co-vertices of the covering triangulation.
48    std::vector<Vector<Rational>> co_points;
49 
50    // Mapping vectors to their position in points
51    Map<Vector<Rational>, Int> vertex_map;
52 
53    // The number of nodes in the dual graph.
54    const Int num_nodes_depth;
55 
56    // The triangles of the triangulation, triplets of corresponding indices.
57    Array<Set<Int>> triangles;
58 
59    // mapping: BFS_node_id -> (half_edge_id, [vec_tail,vec_head])
60    using edge_pair_t = std::pair<Int, Matrix<Rational>>;
61    Map<Int, edge_pair_t> edge_map;
62 
63    Int curr_num_nodes;
64 
65    // A count of the nodes that were visited already.
66    Int num_visited;
67 
68 public:
69 
70    // This is needed for the BFS-iterator to not just stop in depth one.
71    static constexpr bool visit_all_edges = true;
72 
73    /*
74      The PV is initialized with the given graph G (usually only one node for the first triangle (0,0->next,0->next->next) ),
75      the triangulation dcel (containing the A-coordinates),
76      and the depth we want to visit the dual tree.
77    */
PotatoVisitor(Graph<Directed> & G,DoublyConnectedEdgeList & dcel_,const Matrix<Rational> & first_two_vertices,Int dual_tree_depth)78    PotatoVisitor(Graph<Directed>& G, DoublyConnectedEdgeList& dcel_, const Matrix<Rational>& first_two_vertices, Int dual_tree_depth)
79       : dual_tree(G)
80       , dcel(dcel_)
81       , num_nodes_depth(3*((1L << dual_tree_depth)-1)+1)
82       , triangles(num_nodes_depth)
83       , curr_num_nodes(0)
84       , num_visited(0)
85    {
86       firstTriangle(first_two_vertices);
87    }
88 
89 //adding the first triangle, first vectors (half_edge_0 tail and head) are rows of first_two_vertices
firstTriangle(const Matrix<Rational> & first_two_vertices)90 void firstTriangle(const Matrix<Rational>& first_two_vertices)
91 {
92    const HalfEdge* uv = dcel.getHalfEdge(0);
93    const HalfEdge* vu = uv->getTwin();
94    const HalfEdge* vw = uv->getNext();
95    const HalfEdge* wu = vw->getNext();
96    const HalfEdge* uw = wu->getTwin();
97    const Rational& a_uv = uv->getLength();
98    const Rational& a_vu = vu->getLength();
99    const Rational& a_uw = uw->getLength();
100    const Rational& a_vw = vw->getLength();
101    const Face* uvw = uv->getFace();
102    const Rational& a_uvw = uvw->getDetCoord();
103 
104    const Vector<Rational> first_vec = first_two_vertices[0];
105    const Vector<Rational> second_vec = first_two_vertices[1];
106    addVertex(first_vec);
107    addVertex(second_vec);
108 
109    const Vector<Rational> first_co_vec{ Rational(0), a_uv, a_uw/a_uvw };
110    const Vector<Rational> second_co_vec{ a_vu, Rational(0), a_vw/a_uvw };
111    addCoVertex(first_co_vec);
112    addCoVertex(second_co_vec);
113 
114    const Matrix<Rational> M_edge = vector2row(first_vec) / second_vec;
115    edge_map[0] = edge_pair_t(0, M_edge);
116 
117    const Int new_node_id = dual_tree.add_node();
118    dual_tree.add_edge(0, new_node_id);
119    const Matrix<Rational> M_twin_edge = vector2row(second_vec) / first_vec;
120    edge_map[new_node_id] = edge_pair_t(1, M_twin_edge);
121 
122    curr_num_nodes += 2;
123 }
124 
125 /*
126 The vertex vec becomes a new row of the matrix points.
127 The vertex map at vec is set to the corresponding index of the row in points (with index shift due to counting from 0)
128 */
addVertex(const Vector<Rational> & vec)129 void addVertex(const Vector<Rational>& vec)
130 {
131    vertex_map[vec] = points.size();
132    points.push_back(vec);
133 }
134 
addCoVertex(const Vector<Rational> & co_vec)135 void addCoVertex(const Vector<Rational>& co_vec)
136 {
137    co_points.push_back(co_vec);
138 }
139 
140 
numVisited() const141 Int numVisited() const { return num_visited; }
142 
operator ()(Int n)143 bool operator()(Int n)
144 {
145    return operator()(n, n);
146 }
147 
148 
149 // this is what happens when we call bfs++
operator ()(Int n_from,Int n_to)150 bool operator()(Int n_from, Int n_to)
151 {
152    if (visited.contains(n_to)) return false;
153    /*
154      The node n_to was not visited yet, therefore the corresponding triangle (its known halfedge) did not contribute.
155      We extract the known half edge as well as the two known horocycles .
156      Then we compute the position of the third horocycle, and put two new adjacent triangles (half edges) in the queue.
157    */
158    const edge_pair_t& edge_pair = edge_map[n_to];
159    const Vector<Rational> vec_u = edge_pair.second[0];
160    const Vector<Rational> vec_v = edge_pair.second[1];
161    const HalfEdge* uv = dcel.getHalfEdge(edge_pair.first);
162    const HalfEdge* vw = uv->getNext();
163    const HalfEdge* wv = vw->getTwin();
164    const HalfEdge* wu = vw->getNext();
165    const HalfEdge* uw = wu->getTwin();
166    const Rational& a_vw = vw->getLength();
167    const Rational& a_uw = uw->getLength();
168    const Rational& a_wv = wv->getLength();
169    const Rational& a_wu = wu->getLength();
170    const Face* uvw = uv->getFace();
171    const Rational& a_uvw = uvw->getDetCoord();
172    const Vector<Rational>& l_u = co_points[vertex_map[vec_u]];
173    const Vector<Rational>& l_v = co_points[vertex_map[vec_v]];
174    const Vector<Rational> vec_w = thirdVector(vec_u, vec_v, l_u, l_v, a_uw, a_vw, a_uvw);
175    const Vector<Rational> co_vec_w = thirdCoVector(vec_u, vec_v, vec_w, a_wu, a_wv);
176    addVertex(vec_w);
177    addCoVertex(co_vec_w);
178 
179    triangles[n_to] = Set<Int>{ vertex_map[vec_u], vertex_map[vec_v], vertex_map[vec_w] };
180 
181    if (dual_tree.nodes() < num_nodes_depth) {
182       // Add the two new half edges to the queue, update the edge_map and the dual_tree.
183       // Note that we change the sign of the second horocycle (head of wv and uw) since we want to guarantee a positive determinant.
184 
185       const Matrix<Rational> M_wv = vector2row(vec_w) / vec_v;
186       const Matrix<Rational> M_uw = vector2row(vec_u) / vec_w;
187 
188       const Int wv_node_id = dual_tree.add_node();
189       dual_tree.add_edge(n_to, wv_node_id);
190       edge_map[wv_node_id] = edge_pair_t(dcel.getHalfEdgeId(wv), M_wv);
191       const Int uw_node_id = dual_tree.add_node();
192       dual_tree.add_edge(n_to, uw_node_id);
193       edge_map[uw_node_id] = edge_pair_t(dcel.getHalfEdgeId(uw), M_uw);
194 
195       curr_num_nodes += 2;
196    }
197 
198    visited += n_to;
199    ++num_visited;
200 
201    return true;
202 }
203 
getPoints() const204 Matrix<Rational> getPoints() const
205 {
206    return Matrix<Rational>(points);
207 }
208 
getTriangles() const209 const Array<Set<Int>>& getTriangles() const
210 {
211    return triangles;
212 }
213 
getDcel() const214 DoublyConnectedEdgeList& getDcel() const
215 {
216    return dcel;
217 }
218 
thirdVector(const Vector<Rational> & vec_u,const Vector<Rational> & vec_v,const Vector<Rational> & l_u,const Vector<Rational> & l_v,const Rational & a_uw,const Rational & a_vw,const Rational & a_uvw)219 Vector<Rational> thirdVector(const Vector<Rational>& vec_u, const Vector<Rational>& vec_v, const Vector<Rational>& l_u,
220                              const Vector<Rational>& l_v, const Rational& a_uw, const Rational& a_vw, const Rational& a_uvw)
221 {
222    const Vector<Rational> u_cross_v{ vec_u[1]*vec_v[2] - vec_u[2]*vec_v[1],
223                                      vec_u[2]*vec_v[0] - vec_u[0]*vec_v[2],
224                                      vec_u[0]*vec_v[1] - vec_u[1]*vec_v[0] };
225    const Matrix<Rational> A = vector2row(l_u) / l_v / u_cross_v;
226    const Matrix<Rational> A_inv = inv(A);
227    const Vector<Rational> b{ a_uw, a_vw, a_uvw };
228    const Vector<Rational> third_vector = A_inv*b;
229    if (third_vector[0]+third_vector[1]+third_vector[2] < 0)
230       throw std::runtime_error("You should choose a different affine chart");
231    return A_inv*b;
232 }
233 
thirdCoVector(const Vector<Rational> & vec_u,const Vector<Rational> & vec_v,const Vector<Rational> & vec_w,const Rational & a_wu,const Rational & a_wv)234 Vector<Rational> thirdCoVector(const Vector<Rational>& vec_u, const Vector<Rational>& vec_v, const Vector<Rational>& vec_w, const Rational& a_wu, const Rational& a_wv)
235 {
236    const Matrix<Rational> A = vector2row(vec_w) / vec_u / vec_v;
237    const Matrix<Rational> A_inv = inv(A);
238    const Vector<Rational> b{ Rational(0), a_wu, a_wv };
239    return A_inv*b;
240 }
241 
242 }; // end class PotatoVisitor
243 
244 
245 class PotatoBuilder {
246    Graph<Directed> dual_tree; //part of the dual tree of the triangulation
247    Int cur_depth;
248    graph::BFSiterator< Graph<Directed>, graph::VisitorTag<PotatoVisitor> > bfs_it;
249 
250 public:
251 
252    // construct from a dcel and the horo matrix of the first edge
PotatoBuilder(DoublyConnectedEdgeList & dcel,const Matrix<Rational> & first_two_vertices,Int depth_in)253    PotatoBuilder(DoublyConnectedEdgeList& dcel, const Matrix<Rational>& first_two_vertices, Int depth_in)
254       : dual_tree(1)  //start with a one-node graph
255       , cur_depth(depth_in)
256       , bfs_it(dual_tree, PotatoVisitor(dual_tree, dcel, first_two_vertices, depth_in), nodes(dual_tree).front()) {}
257 
getDepth() const258    Int getDepth() const { return cur_depth; }
259 
260    // get the covering triangulation up to a given depth
computeCoveringTriangulation()261    BigObject computeCoveringTriangulation()
262    {
263       Int num_nodes = 3*((1L << cur_depth)-1)+1; //number of nodes of a binary tree with ternary root of given depth
264       while (bfs_it.node_visitor().numVisited() < num_nodes) {
265          ++bfs_it;
266       }
267 
268       const Matrix<Rational> points = ones_vector<Rational>() | bfs_it.node_visitor().getPoints();
269       const Array<Set<Int>> triangles = bfs_it.node_visitor().getTriangles();
270 
271       return BigObject("fan::PolyhedralComplex<Rational>",
272                        "POINTS", points,
273                        "INPUT_POLYTOPES", triangles);
274    }
275 
276 }; // end class PotatoBuilder
277 
278 
279 // Compute a finite part of the triangulation covering the convex RP^2 surface given by A-coordinates "a_coords" on the triangulation "dcel_data".
280 // The rows of the 2x2 matrix "first_two_vertices" are the vertices of the first edge in R^3 that covers the first edge.
281 // The triangulation is calculated up to depth "depth" in the dual spanning tree rooted at the first triangle.
282 // Set "lifted" true in order to produce the concrete decorated triangulation in R^3.
projective_potato(const Matrix<Int> & dcel_data,const Vector<Rational> & a_coords,const Matrix<Rational> & first_two_vertices,Int depth,OptionSet options)283 BigObject projective_potato(const Matrix<Int>& dcel_data, const Vector<Rational>& a_coords, const Matrix<Rational>& first_two_vertices,
284                             Int depth, OptionSet options)
285 {
286    const bool lifted = options["lifted"];
287    DoublyConnectedEdgeList dcel(dcel_data);
288    dcel.setAcoords(a_coords);
289    PotatoBuilder pot(dcel, first_two_vertices, depth);
290    BigObject triang = pot.computeCoveringTriangulation();
291    if (lifted) return triang;
292 
293    const Matrix<Rational> points = triang.give("POINTS");
294    const Matrix<Rational> scaled = dcel.normalize(points.minor(All, range_from(1)));
295    return BigObject("fan::PolyhedralComplex", mlist<Rational>(),
296                     "POINTS", ones_vector<Rational>() | scaled,
297                     "INPUT_POLYTOPES", triang.give("MAXIMAL_POLYTOPES"));
298 }
299 
300 InsertEmbeddedRule("REQUIRE_APPLICATION fan\n\n");
301 UserFunction4perl("# @category Producing other objects\n"
302                   "# Computes the triangulated convex projective set that covers the convex RP^2 surface."
303                   "# The latter is given by the DCEL data for the triangulation of the surface along with A-coordinates (one positive Rational for each oriented edge and each triangle)."
304                   "# Obviously, we only can compute a finite part of the infinite covering triangulation"
305                   "# @param Matrix<Int> DCEL_data"
306                   "# @param Vector<Rational> A_coords"
307                   "# @param Matrix<Rational> first_two_vertices at the moment has to be the Matrix with rows (1,0,0),(0,1,0)"
308                   "# @param Int depth"
309                   "# @option Bool lifted for producing the lifted triangulation in R^3 with vertices in the light cone"
310                   "# @return fan::PolyhedralComplex<Rational>"
311                   "# @example The following computes a covering triangulation of a once punctured torus up to depth 5:"
312                   "# > $T1 = new Matrix<Int>([[0,0,2,3,0,1],[0,0,4,5,0,1],[0,0,0,1,0,1]]);"
313                   "# > $p = projective_potato($T1,new Vector([1,1,1,1,1,1,2,2]),new Matrix([[1,0,0],[0,1,0]]),1);"
314                   "# > print $p->VERTICES;"
315                   "# | 1 1 0 0"
316                   "# | 1 0 1 0"
317                   "# | 1 0 0 1"
318                   "# | 1 1 1 -1"
319                   "# | 1 -1/5 2/5 4/5"
320                   "# | 1 2/5 -1/5 4/5",
321                   &projective_potato, "projective_potato($ $ $ $ {lifted => 0})");
322 } }
323 
324 // Local Variables:
325 // mode:C++
326 // c-basic-offset:3
327 // indent-tabs-mode:nil
328 // End:
329