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/Graph.h"
20 #include "polymake/Set.h"
21 #include "polymake/Vector.h"
22 #include "polymake/Matrix.h"
23 #include "polymake/graph/DoublyConnectedEdgeList.h"
24 #include "polymake/Polynomial.h"
25 #include "polymake/hash_map"
26
27 namespace polymake { namespace topaz {
28
29 using DoublyConnectedEdgeList = graph::dcel::DoublyConnectedEdgeList;
30 using HalfEdge = graph::dcel::HalfEdge;
31
32 using halfedge_variables = Array<Polynomial<Rational, Int>>;
33 using flip_sequence = std::list<Int>;
34
35 // return the outitude polynomial for edge of index edge_id (half edges: 2*edge_id, 2*edge_id+1)
getOutitudePolynomial(const Matrix<Int> & dcel_data,Int edge_id)36 Polynomial<Rational, Int> getOutitudePolynomial(const Matrix<Int>& dcel_data, Int edge_id)
37 {
38 const auto var = Polynomial<Rational, Int>::monomial;
39 DoublyConnectedEdgeList dcel(dcel_data);
40 Int dim = 4*dcel.getNumHalfEdges()/3;
41 Int e = 2*edge_id;
42 const HalfEdge* halfEdge = dcel.getHalfEdge(e);
43 Int a = dcel.getHalfEdgeId( halfEdge->getNext() );
44 Int b = dcel.getHalfEdgeId( halfEdge->getPrev()->getTwin() );
45 Int c = dcel.getHalfEdgeId( halfEdge->getTwin()->getNext() );
46 Int d = dcel.getHalfEdgeId( halfEdge->getTwin()->getPrev()->getTwin() );
47 Int f = dcel.getHalfEdgeId( halfEdge->getTwin() );
48 auto triangle_map = dcel.triangleMap();
49 //auto triangle_map = triangleMap(dcel_data);
50 return var(triangle_map[f],dim)*( var(e,dim)*var(a,dim) + var(f,dim)*var(b,dim) - var(e,dim)*var(f,dim) ) +
51 var(triangle_map[e],dim)*( var(e,dim)*var(d,dim) + var(f,dim)*var(c,dim) - var(e,dim)*var(f,dim) );
52 }
53
54 // given dcel data, return all outitudes, one for each edge
outitudePolynomials(const Matrix<Int> & dcel_data)55 Array<Polynomial<Rational,Int>> outitudePolynomials(const Matrix<Int>& dcel_data)
56 {
57 DoublyConnectedEdgeList dcel(dcel_data);
58 auto outs = Array<Polynomial<Rational, Int>>(dcel.getNumEdges());
59 for (Int i = 0; i < dcel.getNumEdges(); ++i) {
60 outs[i] = getOutitudePolynomial(dcel_data,i);
61 }
62 return outs;
63 }
64
65 UserFunction4perl("# @category Other"
66 "# Given a triangulation of a punctured surface this calculates all the outitude polynomials.\n"
67 "# The first e = #{oriented edges} monomials correspond to A-coordinates of the oriented edges, labeled as in the input.\n"
68 "# The last t = #{triangles} monomials correspond to A-coordinates of the triangles."
69 "# @param Matrix<Int> dcel_data the data for the doubly connected edge list representing the triangulation."
70 "# @return Array<Polynomial<Rational,Int>> an array containing the outitudes in order of the input."
71 "# @example We may calculate the outitude polynomials of a thrice punctured sphere."
72 "# Here the first six monomials x_0, ... , x_5 are associated to the six oriented edges, x_6 and x_7 are associated to the triangles enclosed by the oriented edges 0,2,4 and 1,3,5 respectively."
73 "# > $S3 = new Matrix<Int>([[1,0,2,5,0,1],[2,1,4,1,0,1],[0,2,0,3,0,1]]);;"
74 "# > print outitudePolynomials($S3);"
75 "# | - x_0*x_1*x_6 - x_0*x_1*x_7 + x_0*x_2*x_6 + x_0*x_2*x_7 + x_1*x_5*x_6 + x_1*x_5*x_7 x_1*x_3*x_6 + x_1*x_3*x_7 - x_2*x_3*x_6 - x_2*x_3*x_7 + x_2*x_4*x_6 + x_2*x_4*x_7 x_0*x_4*x_6 + x_0*x_4*x_7 + x_3*x_5*x_6 + x_3*x_5*x_7 - x_4*x_5*x_6 - x_4*x_5*x_7",
76 &outitudePolynomials,"outitudePolynomials( Matrix<Int> )");
77
78
79
80 // return all outitudes, one for each edge
81 /*Array<Polynomial<Rational,Int>> outitudePolynomialsFromString(const std::string& surface)
82 {
83 const char s(surface[0]);
84 Int n;
85 std::istringstream is (surface.substr(1));
86 is >> n;
87 switch(s){
88 case 'S':
89 switch(n){
90 case 3:
91 return outitudePolynomials( {{1,0,2,5,0,1},{2,1,4,1,0,1},{0,2,0,3,0,1}} );
92 //case 4:
93 // return outitudePolynomials( {{1,0,2,6},{2,1,4,9},{0,2,0,11},{3,0,8,5},{1,3,1,10},{2,3,3,7}} );
94 default:
95 throw std::runtime_error("Sorry, so far we cannot handle spheres with more than three punctures.");
96 }
97
98 case 'T':
99 switch(n){
100 case 1:
101 return outitudePolynomials( {{0,0,2,3,0,1},{0,0,4,5,0,1},{0,0,0,1,0,1}} );
102 case 2:
103 return outitudePolynomials( {{0,0,6,5},{0,0,1,10},{0,0,8,2},{1,0,11,4},{1,0,7,3},{1,0,9,0}} );
104 case 3:
105 return outitudePolynomials( {{1,0,2,17},{2,1,4,14},{0,2,0,6},{1,2,8,16},{0,1,5,10},{2,1,12,1},{0,2,9,3},{0,1,13,7},{0,2,15,11}} );
106
107 default:
108 throw std::runtime_error("Sorry, so far we cannot handle a torus with more than one puncture.");
109 }
110
111 case 'D':
112 switch(n){
113 case 1:
114 return outitudePolynomials( {{0,0,2,6},{0,0,4,8},{0,0,0,1},{0,0,5,3},{0,0,7,10},{0,0,16,12},{0,0,14,15},{0,0,11,17},{0,0,9,13}} );
115 default:
116 throw std::runtime_error("Sorry, so far we cannot handle a double torus with more than one puncture. Soon we will.");
117 }
118 default:
119 throw std::runtime_error("Did not recognize your surface.");
120 }
121
122 }
123 UserFunction4perl("# @category Other"
124 "# Given a punctured surface by a string from the list below, this calculates all the outitude polynomials.\n"
125 "# Choose among: S3, S4 (punctured spheres) and T1, T2, T3 (punctured tori) and D1 (punctured double torus).\n"
126 "# A triangulation of the surface will be chosen for you.\n"
127 "# The first e = #{oriented edges} monomials correspond to A-coordinates of the oriented edges.\n"
128 "# The last t = #{triangles} monomials correspond to A-coordinates of the triangles."
129 "# @param String surface the surface name."
130 "# @return Array<Polynomial<Rational,Int>> an array containing the outitudes." ,
131 &outitudePolynomialsFromString,"outitudePolynomialsFromString( String )");
132 */
133
134
135 // return the dual outitude polynomial for edge of index edge_id (half edges: 2*edge_id, 2*edge_id+1)
getDualOutitudePolynomial(const Matrix<Int> & dcel_data,Int edge_id)136 Polynomial<Rational,Int> getDualOutitudePolynomial(const Matrix<Int>& dcel_data, Int edge_id)
137 {
138 const auto var = Polynomial<Rational, Int>::monomial;
139
140 DoublyConnectedEdgeList dcel(dcel_data);
141 Int dim = 4*dcel.getNumHalfEdges()/3;
142 Int e = 2*edge_id;
143 const HalfEdge* halfEdge = dcel.getHalfEdge(e);
144 Int a = dcel.getHalfEdgeId( halfEdge->getNext() );
145 Int aa = dcel.getHalfEdgeId( halfEdge->getNext()->getTwin() );
146 Int b = dcel.getHalfEdgeId( halfEdge->getPrev()->getTwin() );
147 Int bb = dcel.getHalfEdgeId( halfEdge->getPrev() );
148 Int c = dcel.getHalfEdgeId( halfEdge->getTwin()->getNext() );
149 Int cc = dcel.getHalfEdgeId( halfEdge->getTwin()->getNext()->getTwin() );
150 Int d = dcel.getHalfEdgeId( halfEdge->getTwin()->getPrev()->getTwin() );
151 Int dd = dcel.getHalfEdgeId( halfEdge->getTwin()->getPrev() );
152 Int f = dcel.getHalfEdgeId( halfEdge->getTwin() );
153
154 auto triangle_map = dcel.triangleMap();
155 Int A = triangle_map[e];
156 Int B = triangle_map[f];
157
158 return var(A,dim)*( var(e,dim)*var(cc,dim)*var(d,dim) + var(f,dim)*var(c,dim)*var(dd,dim) )*( var(f,dim)*var(aa,dim) + var(e,dim)*var(bb,dim) - var(e,dim)*var(f,dim) ) +
159 var(B,dim)*( var(e,dim)*var(a,dim)*var(bb,dim) + var(f,dim)*var(aa,dim)*var(b,dim) )*( var(f,dim)*var(dd,dim) + var(e,dim)*var(cc,dim) - var(e,dim)*var(f,dim) );
160 }
161 //Function4perl(&getDualOutitudePolynomial,"getDualOutitudePolynomial( $$ )");
162
163
164
165 // return all dual outitudes, one for each edge
dualOutitudePolynomials(const Matrix<Int> & dcel_data)166 Array<Polynomial<Rational,Int>> dualOutitudePolynomials(const Matrix<Int>& dcel_data)
167 {
168 DoublyConnectedEdgeList dcel(dcel_data);
169 auto outs = Array<Polynomial<Rational, Int>>(dcel.getNumEdges());
170 for (Int i = 0; i < dcel.getNumEdges(); i++){
171 outs[i] = getDualOutitudePolynomial(dcel_data,i);
172 }
173 return outs;
174 }
175 UserFunction4perl("# @category Other"
176 "# Given a triangulation of a punctured surface this calculates all the outitude polynomials of the dual structure.\n"
177 "# The first e = #{oriented edges} monomials correspond to A-coordinates of the oriented edges of the primal structure , labeled as in the input.\n"
178 "# The last t = #{triangles} monomials correspond to A-coordinates of the triangles of the primal structure."
179 "# @param Matrix<Int> dcel_data the data for the doubly connected edge list representing the triangulation."
180 "# @return Array<Polynomial<Rational,Int>> an array containing the dual outitudes in order of the input.",
181 &dualOutitudePolynomials,"dualOutitudePolynomials( $ )");
182
183
184
185
outitudes_from_dcel(const DoublyConnectedEdgeList & dcel)186 Vector<Rational> outitudes_from_dcel(const DoublyConnectedEdgeList& dcel)
187 {
188 Vector<Rational> out_vec(dcel.getNumEdges());
189 for (Int i = 0; i < dcel.getNumEdges(); ++i) {
190 const HalfEdge* e_edge = dcel.getHalfEdge(2*i);
191 const HalfEdge* e_twin = e_edge->getTwin();
192 const Rational& e_plus = e_edge->getLength();
193 const Rational& e_minus = e_twin->getLength();
194 const Rational& a = e_edge->getNext()->getLength();
195 const Rational& b = e_edge->getPrev()->getTwin()->getLength();
196 const Rational& c = e_twin->getNext()->getLength();
197 const Rational& d = e_twin->getPrev()->getTwin()->getLength();
198 const Rational& A = e_twin->getFace()->getDetCoord();
199 const Rational& B = e_edge->getFace()->getDetCoord();
200 out_vec[i] = A*(e_plus*a+e_minus*b-e_plus*e_minus) + B*(e_plus*d+e_minus*c-e_plus*e_minus);
201 }
202 return out_vec;
203 }
204
205
is_canonical(const DoublyConnectedEdgeList & dcel)206 std::pair<Set<Int>, Set<Int>> is_canonical(const DoublyConnectedEdgeList& dcel)
207 {
208 Set<Int> concave_edges;
209 Set<Int> flat_edges;
210
211 Vector<Rational> out_vec = outitudes_from_dcel(dcel);
212
213 for (Int i = 0; i < out_vec.size(); ++i) {
214 Rational out_i = out_vec[i];
215 if (out_i < 0)
216 concave_edges += i;
217 else if (out_i == 0)
218 flat_edges += i;
219 }
220 return { concave_edges, flat_edges };
221 }
222
223
224
flip_coords(DoublyConnectedEdgeList & dcel,Vector<Rational> coords,Int edge_id)225 Vector<Rational> flip_coords(DoublyConnectedEdgeList& dcel, Vector<Rational> coords, Int edge_id)
226 {
227 Vector<Rational> new_coords(coords);
228 Int e_plus_id = 2*edge_id;
229 Int e_minus_id = 2*edge_id+1;
230 const HalfEdge* halfEdge = dcel.getHalfEdge(e_plus_id);
231 Int A_id = dcel.getFaceId(halfEdge->getFace());
232 Int B_id = dcel.getFaceId(halfEdge->getTwin()->getFace());
233 Int a_plus_id = dcel.getHalfEdgeId(halfEdge->getNext());
234 Int a_minus_id = dcel.getHalfEdgeId(halfEdge->getNext()->getTwin());
235 Int b_plus_id = dcel.getHalfEdgeId(halfEdge->getNext()->getNext());
236 Int b_minus_id = dcel.getHalfEdgeId(halfEdge->getNext()->getNext()->getTwin());
237 Int c_minus_id = dcel.getHalfEdgeId(halfEdge->getTwin()->getNext());
238 Int c_plus_id = dcel.getHalfEdgeId(halfEdge->getTwin()->getNext()->getTwin());
239 Int d_minus_id = dcel.getHalfEdgeId(halfEdge->getTwin()->getNext()->getNext());
240 Int d_plus_id = dcel.getHalfEdgeId(halfEdge->getTwin()->getNext()->getNext()->getTwin());
241
242 Rational C = (coords[A_id]*coords[c_minus_id] + coords[B_id]*coords[b_minus_id])/coords[e_plus_id];
243 Rational D = (coords[A_id]*coords[d_plus_id] + coords[B_id]*coords[a_plus_id])/coords[e_minus_id];
244 Rational f_plus = (C*coords[d_minus_id] + D*coords[c_plus_id])/coords[B_id];
245 Rational f_minus = (C*coords[a_minus_id] + D*coords[b_plus_id])/coords[A_id];
246
247 new_coords[e_plus_id] = f_plus;
248 new_coords[e_minus_id] = f_minus;
249 new_coords[A_id] = C;
250 new_coords[B_id] = D;
251
252 //halfEdge->setLength(f_plus);
253 //dcel.getHalfEdge(e_minus_id).setLength(f_minus);
254 //dcel.getFaces()[A_id].setDetCoord(C);
255 //dcel.getFaces()[B_id].setDetCoord(D);
256 return new_coords;
257 }
258
259
260
flips_to_canonical_triangulation(const Matrix<Int> & dcel_data,Vector<Rational> & a_coords)261 std::pair<flip_sequence, Set<Int>> flips_to_canonical_triangulation(const Matrix<Int>& dcel_data, Vector<Rational>& a_coords)
262 {
263 DoublyConnectedEdgeList dcel(dcel_data,a_coords);
264 Vector<Rational> curr_a_coords(a_coords);
265
266 flip_sequence flips;
267 auto stuff = is_canonical(dcel);
268 auto concave_edges = stuff.first;
269 auto flat_edges = stuff.second;
270 while (!concave_edges.empty()) {
271 Int bad_edge = concave_edges.front();
272 Vector<Rational> new_coords = flip_coords(dcel,curr_a_coords,bad_edge);
273 dcel.flipEdgeWithFaces(bad_edge);
274 flips.push_back(bad_edge);
275 stuff = is_canonical(dcel);
276 concave_edges = stuff.first;
277 flat_edges = stuff.second;
278 }
279 return { flips, flat_edges };
280 }
281
282 UserFunction4perl("# @category Other\n"
283 "# Computes a flip sequence to a canonical triangulation (first list)."
284 "# The second output is a list of flat edges in the canonical triangulation."
285 "# @param Matrix<Int> DCEL_data"
286 "# @param Vector<Rational> A_coords"
287 "# @return Pair<List<Int>,Set<Int>>"
288 "# @example In the following example only edge 2 has negative outitude, so the flip sequence should start with 2. After performing this flip, the triangulation thus obtained is canonical."
289 "# > $T1 = new Matrix<Int>([[0,0,2,3,0,1],[0,0,4,5,0,1],[0,0,0,1,0,1]]);"
290 "# > print flips_to_canonical_triangulation($T1,[1,2,3,4,5,6,1,2]);"
291 "# | {2} {}",
292 &flips_to_canonical_triangulation,"flips_to_canonical_triangulation($$)");
293
294
out(Matrix<Int> dcel_data,Vector<Rational> a_coords,Int edge_id)295 Rational out(Matrix<Int> dcel_data, Vector<Rational> a_coords, Int edge_id)
296 {
297 DoublyConnectedEdgeList dcel(dcel_data, a_coords);
298 const HalfEdge* e_edge = dcel.getHalfEdge(2*edge_id);
299 const HalfEdge* e_twin = e_edge->getTwin();
300 Rational e_plus = e_edge->getLength();
301 Rational e_minus = e_twin->getLength();
302 Rational a = e_edge->getNext()->getLength();
303 Rational b = e_edge->getPrev()->getTwin()->getLength();
304 Rational c = e_twin->getNext()->getLength();
305 Rational d = e_twin->getPrev()->getTwin()->getLength();
306 Rational A = e_twin->getFace()->getDetCoord();
307 Rational B = e_edge->getFace()->getDetCoord();
308 return A*(e_plus*a+e_minus*b-e_plus*e_minus) + B*(e_plus*d+e_minus*c-e_plus*e_minus);
309 }
310
outitudes(Matrix<Int> dcel_data,Vector<Rational> a_coords)311 Vector<Rational> outitudes(Matrix<Int> dcel_data, Vector<Rational> a_coords)
312 {
313 Vector<Rational> out_vec(dcel_data.rows());
314 for (Int i = 0; i < dcel_data.rows(); ++i) {
315 out_vec[i] = out(dcel_data,a_coords, i);
316 }
317 return out_vec;
318 }
319
320 UserFunction4perl("# @category Producing other objects\n"
321 "# Computes the outitudes along edges."
322 "# @param Matrix<Int> DCEL_data"
323 "# @param Vector<Rational> A_coords"
324 "# @return Vector<Rational>"
325 "# @example In the following example only edge 2 has negative outitude."
326 "# > $T1 = new Matrix<Int>([[0,0,2,3,0,1],[0,0,4,5,0,1],[0,0,0,1,0,1]]);"
327 "# > print outitudes($T1,[1,2,3,4,5,6,1,2]);"
328 "# | 37 37 -5",
329 &outitudes, "outitudes($ $)");
330
331
332
333 } //end topaz namespace
334 } //end polymake namespace
335
336 // Local Variables:
337 // mode:C++
338 // c-basic-offset:3
339 // indent-tabs-mode:nil
340 // End:
341