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 Functions to compute the combinatorics of rational n-marked curves, e.g. 27 the metric-to-tree algorithm by Buneman 28 */ 29 30 31 #include "polymake/client.h" 32 #include "polymake/Rational.h" 33 #include "polymake/Matrix.h" 34 #include "polymake/Vector.h" 35 #include "polymake/Set.h" 36 #include "polymake/Map.h" 37 #include "polymake/PowerSet.h" 38 #include "polymake/Array.h" 39 #include "polymake/linalg.h" 40 #include "polymake/IncidenceMatrix.h" 41 #include "polymake/Graph.h" 42 #include "polymake/permutations.h" 43 #include "polymake/TropicalNumber.h" 44 #include "polymake/tropical/misc_tools.h" 45 46 namespace polymake { namespace tropical { 47 48 Int moduliDimensionFromLength(Int length) 49 { 50 Int s = Int(sqrt(1 + 8*length)); 51 Int r = (1+s)/2; 52 // Test for validity 53 if ((r*(r-1)/2) != length) { 54 throw std::runtime_error("Length is not of the form (n over 2)"); 55 } 56 return r; 57 } 58 59 /** 60 @brief Takes three rational values and checks whether two of them are equal and >= than the third 61 */ 62 bool fpcCheck(const Rational& a, const Rational& b, const Rational& c) 63 { 64 if (a == b && a >= c) return true; 65 if (a == c && a >= b) return true; 66 if (b == c && b >= a) return true; 67 return false; 68 } 69 70 /////////////////////////////////////////////////////////////////////////////////////// 71 72 // Documentation see perl wrapper of wrapTestFourPointCondition (does the same except that it returns 73 // a vector of Int) 74 Vector<Int> testFourPointCondition(const Vector<Rational>& v) 75 { 76 // Convert metric into map 77 Int n = moduliDimensionFromLength(v.dim()); 78 Matrix<Rational> d(n+1,n+1); 79 80 Int mindex = 0; 81 for (Int i = 1; i < n; ++i) { 82 for (Int j = i+1; j <= n; ++j) { 83 d(i,j) = d(j,i) = v[mindex]; 84 ++mindex; 85 } 86 } 87 88 // First we test all 4-element subsets 89 const auto complete = sequence(1,n); 90 if (n >= 4) { 91 for (auto fours = entire(all_subsets_of_k(complete,4)); !fours.at_end(); ++fours) { 92 const auto& l = *fours; 93 Rational a = d(l[0],l[1]) + d(l[2],l[3]); 94 Rational b = d(l[0],l[2]) + d(l[1],l[3]); 95 Rational c = d(l[0],l[3]) + d(l[1],l[2]); 96 // Check that two of a,b,c are equal and not less than the third 97 if (!fpcCheck(a,b,c)) { 98 Vector<Int> fault(l); 99 return fault; 100 } 101 } 102 } 103 104 // Now we check all 3-element subsets 105 for (auto threes = entire(all_subsets_of_k(complete,3)); !threes.at_end(); ++threes) { 106 const auto& l = *threes; 107 // Now check the three possibilities, where the fourth element is equal to any of the three 108 for (Int t = 0; t < 3; ++t) { 109 Rational a = d(l[0],l[1]) + d(l[2],l[t]); 110 Rational b = d(l[0],l[2]) + d(l[1],l[t]); 111 Rational c = d(l[0],l[t]) + d(l[1],l[2]); 112 // Check that two of a,b,c are equal and not less than the third 113 if (!fpcCheck(a,b,c)) { 114 Vector<Int> fault(4); 115 fault[0] = l[0]; 116 fault[1] = l[1]; 117 fault[2] = l[2]; 118 fault[3] = l[t]; 119 return fault; 120 } 121 } 122 } 123 124 // Now we check all 2-element subsets 125 for (auto twos = entire(all_subsets_of_k(complete,2)); !twos.at_end(); ++twos) { 126 const auto& l = *twos; 127 // We have three possibilites for the other two z,t: t=x,z=y or t=z=x or t=z=y 128 for (Int p = 1; p <= 3; ++p) { 129 Int t = p < 3 ? l[0] : l[1]; 130 Int z = p != 2 ? l[1] : l[0]; 131 Rational a = d(l[0],l[1]) + d(z,t); 132 Rational b = d(l[0],z) + d(l[1],t); 133 Rational c = d(l[0],t) + d(l[1],z); 134 // Check that two of a,b,c are equal and not less than the third 135 if (!fpcCheck(a,b,c)) { 136 Vector<Int> fault(4); 137 fault[0] = l[0]; 138 fault[1] = l[1]; 139 fault[2] = z; 140 fault[3] = t; 141 return fault; 142 } 143 } 144 } 145 return Vector<Int>{}; 146 } 147 148 /////////////////////////////////////////////////////////////////////////////////////// 149 150 // Documentation see perl wrapper 151 ListReturn wrapTestFourPointCondition(const Vector<Rational>& v) 152 { 153 Vector<Int> fault = testFourPointCondition(v); 154 ListReturn result; 155 // TODO: implement unroll for ListReturn 156 for (Int i = 0; i < fault.dim(); ++i) { 157 result << fault[i]; 158 } 159 return result; 160 } 161 162 /////////////////////////////////////////////////////////////////////////////////////// 163 164 /** 165 @brief Computes a rational curve (in the v_I-representation) and its graph from a given metric (or more precisely a vector equivalent to a metric). It is wrapped by curveFromMetric and graphFromMetric, which should be called instead and whose documentation can be found in the corr. perl wrappers rational_curve_from_metric and curve_graph_from_metric. Note that the order of the [[EDGES]] of the graph object that do not contain leaf vertices is the same as the order of the corresponding [[SETS]] of the curve. Furthermore, the first n vertices of the graph are the end vertices of the leave edges (in order 1 .. n) 166 */ 167 BigObject curveAndGraphFromMetric(Vector<Rational> metric) 168 { 169 // We prepare the metric by making sure, all entries are > 0 170 const Int n = moduliDimensionFromLength(metric.dim()); 171 172 // We now add Phi(sum of unit vectors) to the metric until it fulfills the four-point-condition 173 // and is positive 174 // Then we add it once more to make sure every leaf has positive distance to its vertex 175 // Note that adding Phi(..) is the same as adding 2 to every entry in the matrix 176 bool hasBeenfpced = false; 177 bool hasBeenStretched = false; 178 Int tryCount = 0; 179 while (!(hasBeenfpced && hasBeenStretched)) { 180 // Since we cannot predict, how many tries we need to ensure 4-point-condition, 181 // we insert a maximum bound to avoid endless looping on metrics that don't lie in M_n 182 if (tryCount >= 1000000) { 183 throw std::runtime_error("Cannot make metric four-point-condition-compatible: Maybe it's not in the moduli space?"); 184 } 185 metric += 2*ones_vector<Rational>(metric.dim()); 186 // Check if everything is positive 187 if (accumulate(Set<Rational>(metric), operations::min()) <= 0) { 188 continue; 189 } 190 // If it already had been 4-point-cond-compatible and positive, this was the stretching 191 if (hasBeenfpced) hasBeenStretched = true; 192 // Check if it fulfills the 4-pc 193 if (testFourPointCondition(metric).dim() == 0) 194 hasBeenfpced = true; 195 else 196 ++tryCount; 197 } 198 199 // For simplicity we ignore the first row and column and start counting at 1 200 Matrix<Rational> d(n+1, n+1); 201 Int mindex = 0; 202 for (Int i = 1; i < n; ++i) { 203 for (Int j = i+1; j <= n; ++j) { 204 d(i,j) = metric[mindex]; 205 d(j,i) = metric[mindex]; 206 ++mindex; 207 } 208 } 209 210 // For the graph case, we prepare a graph object 211 Graph<> G(n); 212 213 // To make the order of SETS/COEFFS agree with the order of the edges in graph, 214 // we keep track of the original graph edge order. 215 Vector<std::pair<Int, Int>> edge_order; 216 217 // The algorithm possibly produces "double" vertices when creating a new vertex t 218 // that has distance 0 to p or q. In this case we have to remember the original index p (or q) 219 // to be able to create the graph correctly 220 // So, at position i, it gives the number of the vertex (starting at 1), the virtual vertex i 221 // actually represents. When creating a new vertex, this should be the maximum over the list + 1 222 Vector<Int> orig(n+1); 223 for (Int i = 1; i <= n; ++i) 224 orig[i] = i; 225 226 // Result variable 227 Vector<Rational> coeffs; 228 Vector<Set<Int>> sets; 229 // Prepare vertex set, leaf map 230 Set<Int> V = sequence(1, n); 231 Map<Int, Set<Int>> leaves; 232 for (Int i = 1; i <= n; ++i) { 233 Set<Int> singleset; singleset += i; 234 leaves[i] = singleset; 235 } 236 // These variables will contain the node data 237 Vector<Set<Int>> nodes_by_leaves(n), nodes_by_sets(n); 238 239 // Now inductively remove pairs of vertices until only 3 are left 240 while (V.size() > 3) { 241 // Find the triple (p,q,r) that maximizes the Buneman term 242 Int p = 0, q = 0, r = 0; 243 bool init = false; 244 Rational max = 0; 245 for (auto a = entire(V); !a.at_end(); ++a) { 246 for (auto b = entire(V); !b.at_end(); ++b) { 247 if (*b != *a) { 248 for (auto c = entire(V); !c.at_end(); ++c) { 249 if (*c != *b && *c != *a) { 250 Rational newd = d(*a,*c) + d(*b,*c) - d(*a,*b); 251 if (newd > max || !init) { 252 max = newd; 253 p = *a; q = *b; r = *c; 254 init = true; 255 } 256 } 257 } 258 } 259 } 260 } // End find maximal triple 261 262 // Compute distances to the new virtual element t 263 Rational dtp = (d(p,q) + d(p,r) - d(q,r)) / 2; 264 Vector<Rational> dtx(d.cols()); // Again, start counting from 1 265 Int x = 0; 266 for (auto i = entire(V); !i.at_end(); ++i) { 267 if (*i != p) { 268 dtx[*i] = d(*i,p) - dtp; 269 if (*i != q && dtx[*i] == 0) { 270 x = *i; 271 } 272 } 273 } 274 V -= p; 275 V -= q; 276 277 // Now 'add' the new vertex 278 if (x> 0) { 279 leaves[x] = leaves[x] + leaves[p] + leaves[q]; 280 if (leaves[p].size() > 1 && leaves[p].size() < n-1) { 281 coeffs |= d(p,x); 282 sets |= leaves[p]; 283 nodes_by_sets[orig[p]-1] += (sets.dim()-1); 284 nodes_by_sets[orig[x]-1] += (sets.dim()-1); 285 } 286 if (leaves[q].size() > 1 && leaves[q].size() < n-1) { 287 coeffs |= d(q,x); 288 sets |= leaves[q]; 289 nodes_by_sets[orig[q]-1] += (sets.dim()-1); 290 nodes_by_sets[orig[x]-1] += (sets.dim()-1); 291 } 292 // If p or q are leaves, add them to the node leaves of x 293 if (leaves[p].size() == 1) nodes_by_leaves[orig[x]-1] += leaves[p]; 294 if (leaves[q].size() == 1) nodes_by_leaves[orig[x]-1] += leaves[q]; 295 // Graph case 296 G.edge(orig[p]-1,orig[x]-1); 297 G.edge(orig[q]-1,orig[x]-1); 298 edge_order |= std::pair<Int, Int>(orig[p]-1,orig[x]-1); 299 edge_order |= std::pair<Int, Int>(orig[q]-1,orig[x]-1); 300 } 301 else { 302 // Note: It can not be possible that t=q and q is a leaf, since the removal 303 // of p would then yield a disconnected graph 304 // Graph case 305 // If d(t,p) or d(t,q) = 0, identify t with p (or q) 306 // Otherwise give t the next available node index 307 if (dtp != 0 && dtx[q] != 0) { 308 Int node_number = G.add_node(); 309 orig |= (node_number+1); 310 nodes_by_leaves |= Set<Int>(); 311 nodes_by_sets |= Set<Int>(); 312 } 313 if (dtp == 0) { 314 orig |= orig[p]; 315 } 316 if (dtx[q] == 0) { 317 orig |= orig[q]; 318 } 319 // We update the distance matrix, since we add a new element 320 d = d | zero_vector<Rational>(); 321 d = d / zero_vector<Rational>(); 322 Int t = d.cols() -1; 323 for (auto i = entire(V); !i.at_end(); i++) { 324 d(*i,t) = d(t,*i) = dtx[*i]; 325 } 326 327 // Now add the new vertex 328 V += t; 329 leaves[t] = leaves[p] + leaves[q]; 330 331 if (leaves[p].size() > 1 && leaves[p].size() < n-1) { 332 if (dtp != 0) { 333 coeffs |= dtp; 334 sets |= leaves[p]; 335 nodes_by_sets[orig[p]-1] += (sets.dim()-1); 336 nodes_by_sets[orig[t]-1] += (sets.dim()-1); 337 } 338 } 339 if (leaves[q].size() > 1 && leaves[q].size() < n-1) { 340 if (dtx[q] != 0) { 341 coeffs |= dtx[q]; 342 sets |= leaves[q]; 343 nodes_by_sets[orig[q]-1] += (sets.dim()-1); 344 nodes_by_sets[orig[t]-1] += (sets.dim()-1); 345 } 346 } 347 348 // Now add the leaves 349 if (leaves[p].size() == 1) nodes_by_leaves[orig[t]-1] += leaves[p]; 350 if (leaves[q].size() == 1) nodes_by_leaves[orig[t]-1] += leaves[q]; 351 352 if (dtp != 0) { 353 G.edge(orig[t]-1,orig[p]-1); 354 edge_order |= std::pair<Int, Int>(orig[t]-1,orig[p]-1); 355 } 356 if (dtx[q] != 0) { 357 G.edge(orig[t]-1,orig[q]-1); 358 edge_order |= std::pair<Int, Int>(orig[t]-1,orig[q]-1); 359 } 360 } 361 } // End while(>3) 362 363 // Now treat the basic cases of size 2 and 3 364 Vector<Int> vAsList(V); 365 if (V.size() == 3) { 366 // Solve the linear system given by the pairwise distances 367 Matrix<Rational> A(3,3); 368 // Create the inverse matrix of the distance relation and multiply it with the distance vectors 369 A(0,0) = A(0,1) = A(1,0) = A(1,2) = A(2,1) = A(2,2) = 0.5; 370 A(0,2) = A (1,1) = A(2,0) = -0.5; 371 Vector<Rational> B(3); 372 B[0] = d(vAsList[0],vAsList[1]); 373 B[1] = d(vAsList[0],vAsList[2]); 374 B[2] = d(vAsList[1],vAsList[2]); 375 Vector<Rational> a = A * B; 376 Int zeroa = -1; 377 Array<Int> setsindices(3); //Indices of partitions in variable sets 378 for (Int i = 0; i < 3; ++i) { 379 setsindices[i] = -1; 380 if (a[i] != 0) { 381 if (leaves[vAsList[i]].size() > 1 && leaves[vAsList[i]].size() < n-1) { 382 coeffs |= a[i]; 383 sets |= leaves[vAsList[i]]; 384 setsindices[i] = sets.dim()-1; 385 } 386 } else { 387 zeroa = i; 388 } 389 } 390 // Graph case 391 // If all distances are nonzero, we add a vertex 392 if (zeroa == -1) { 393 Int t = G.add_node(); 394 nodes_by_leaves |= Set<Int>(); 395 nodes_by_sets |= Set<Int>(); 396 for (Int j = 0; j < 3; ++j) { 397 G.edge(t,orig[vAsList[j]]-1); 398 edge_order |= std::pair<Int, Int>(t,orig[vAsList[j]]-1); 399 if (leaves[vAsList[j]].size() == 1) { 400 nodes_by_leaves[t] += leaves[vAsList[j]]; 401 } else { 402 nodes_by_sets[t] += setsindices[j]; 403 nodes_by_sets[orig[vAsList[j]]-1] += setsindices[j]; 404 } 405 } 406 } 407 // Otherwise we add the adjacencies of the two egdes 408 else { 409 for (Int j = 0; j < 3; ++j) { 410 if (j != zeroa) { 411 G.edge(orig[vAsList[j]]-1,orig[vAsList[zeroa]]-1); 412 edge_order |= std::pair<Int, Int>(orig[vAsList[j]]-1,orig[vAsList[zeroa]]-1); 413 if (leaves[vAsList[j]].size() == 1) { 414 nodes_by_leaves[orig[vAsList[zeroa]]-1] += leaves[vAsList[j]]; 415 } else { 416 nodes_by_sets[orig[vAsList[zeroa]]-1] += setsindices[j]; 417 nodes_by_sets[orig[vAsList[j]]-1] += setsindices[j]; 418 } 419 } 420 } 421 } 422 } // End case size == 3 423 424 if (V.size() == 2) { 425 // Two remaining nodes forming a tree cannot both be leaves of the original tree 426 // If necessary, we swap the list, so that the first element is the non-leaf 427 if (leaves[vAsList[0]].size() == 1) { 428 std::swap(vAsList[0],vAsList[1]); 429 } 430 // We only have a bounded edge, if the second element is also not a leaf 431 if (leaves[vAsList[1]].size() > 1 && leaves[vAsList[1]].size() < n-1) { 432 coeffs |= d(vAsList[0],vAsList[1]); 433 sets |= leaves[vAsList[0]]; 434 nodes_by_sets[orig[vAsList[0]]-1] += (sets.dim()-1); 435 nodes_by_sets[orig[vAsList[1]]-1] += (sets.dim()-1); 436 } 437 // Otherwise we add a leaf at the first element 438 else { 439 nodes_by_leaves[orig[vAsList[0]]-1] += leaves[vAsList[1]]; 440 } 441 // Graph case 442 G.edge(orig[vAsList[0]]-1,orig[vAsList[1]]-1); 443 edge_order |= std::pair<Int, Int>(orig[vAsList[0]]-1,orig[vAsList[1]]-1); 444 } 445 446 // Now we're done, so we create the result 447 448 // Create node labels 449 Array<std::string> labels(G.nodes()); 450 for (Int i = 0; i < n; ++i) { 451 labels[i] = std::to_string(i+1); 452 } 453 454 // Compute node degrees 455 nodes_by_leaves = nodes_by_leaves.slice(~sequence(0,n)); 456 nodes_by_sets = nodes_by_sets.slice(~sequence(0,n)); 457 Vector<Int> node_degrees(nodes_by_leaves.dim()); 458 for (Int nn = 0; nn < node_degrees.dim(); ++nn) { 459 node_degrees[nn] = nodes_by_leaves[nn].size() + nodes_by_sets[nn].size(); 460 } 461 462 BigObject graph("graph::Graph", 463 "N_NODES", G.nodes(), 464 "ADJACENCY", G, 465 "NODE_LABELS", labels); 466 467 // The edges in G might now have a different order than the one in which we put it in 468 // Hence we have to make sure, the order of SETS and COEFFS is compatible with the EDGES order 469 // For this we have kept edge_order as a record of the original order of edges. 470 const Array<Set<Int>> edge_list = graph.call_method("EDGES"); 471 // First we throw out all the edges in the original ordering that are actually leaves 472 Set<Int> leaf_edges; 473 for (Int oe = 0; oe < edge_order.dim(); ++oe) { 474 if (edge_order[oe].first < n || edge_order[oe].second < n) 475 leaf_edges += oe; 476 } 477 edge_order = edge_order.slice(~leaf_edges); 478 479 // FIXME: misuse of Vector concatenation 480 Vector<Set<Int>> ordered_sets; 481 Vector<Rational> ordered_coeffs; 482 for (const auto& edge : edge_list) { 483 // First, see if it has an entry < n. Then its actually a leaf, not a bounded edge 484 if (edge.front() >= n) { 485 // Check which edge (in the original ordering) agrees with this one 486 Int oeindex = -1; 487 for (Int oe = 0; oe < edge_order.dim(); ++oe) { 488 if (edge.contains(edge_order[oe].first) && edge.contains(edge_order[oe].second)) { 489 oeindex = oe; 490 break; 491 } 492 } 493 ordered_sets |= sets[oeindex]; 494 ordered_coeffs |= coeffs[oeindex]; 495 } 496 } // END re-order sets and coeffs 497 498 if (sets.dim() == 0) { 499 sets |= Set<Int>(); 500 coeffs |= 0; 501 } 502 503 return BigObject("RationalCurve", 504 "SETS", ordered_sets, 505 "COEFFS", ordered_coeffs, 506 "N_LEAVES", n, 507 "GRAPH", graph, 508 "GRAPH_EDGE_LENGTHS", ordered_coeffs, 509 "NODES_BY_LEAVES", nodes_by_leaves, 510 "NODES_BY_SETS", nodes_by_sets, 511 "NODE_DEGREES", node_degrees); 512 } 513 514 // Documentation see perl wrapper 515 BigObject curveFromMetric(const Vector<Rational>& metric) 516 { 517 return curveAndGraphFromMetric(metric); 518 } 519 520 521 /* 522 * @brief Takes a vector from Q^(n over 2) that describes an n-marked rational abstract 523 * curve as a distance vector between its leaves. It then computes the 524 * graph of the curve corresponding to this vector. 525 * @param Vector<Rational> v A vector of length (n over 2). Its entries are 526 * interpreted as the distances d(i,j) ordered lexicographically according to i,j. However, they need not be positive, as long as v is equivalent to a proper 527 * metric modulo leaf lengths. 528 * @return An array containing first the graph::Graph and then a Vector<Rational>, containing 529 * the lengths of the bounded edges (in the order they appear in EDGES) 530 */ 531 ListReturn graphFromMetric(const Vector<Rational>& metric) 532 { 533 BigObject curve = curveAndGraphFromMetric(metric); 534 BigObject graph = curve.give("GRAPH"); 535 Vector<Rational> lengths = curve.give("COEFFS"); 536 ListReturn result; 537 result << graph.copy(); 538 result << lengths; 539 return result; 540 } 541 542 543 /** 544 @brief Takes a linear combination of abstract n-marked curves with 1 bounded edge, described by their partitions and the corresponding edge length and computes the resulting metric 545 @param IncidenceMatrix<> sets A list of partitions of {1,..,n}. May be redundant. 546 @param Vector<Set<Int>> coeffs A list of arbitrary rational coefficients. Superfluous coefficients are ignored, missing ones replaced by 0. 547 @param Int n The size of the leaf set 548 @return Vector<Rational> A curve metric of length (n over 2) 549 */ 550 Vector<Rational> metricFromCurve(const IncidenceMatrix<>& sets, const Vector<Rational>& coeffs, Int n) 551 { 552 // Create distance matrix (we count from 1 for simplicity) 553 Matrix<Rational> d(n+1, n+1); 554 const auto completeSet = sequence(1,n); 555 // Go through all sets 556 for (Int s = 0; s < sets.rows(); ++s) { 557 // If we have no more coefficients, stop calculating 558 if (s >= coeffs.dim()) break; 559 // Otherwise add the coefficients to the appropriate distances 560 Rational c = coeffs[s]; 561 const auto& sset = sets.row(s); 562 Set<Int> complement = completeSet - sset; 563 for (const Int selt : sset) { 564 for (const Int celt : complement) { 565 d(selt, celt) += c; 566 d(celt, selt) += c; 567 } 568 } 569 } 570 571 // Now convert to a vector 572 Vector<Rational> result; 573 for (Int i = 1; i < n; ++i) { 574 for (Int j = i+1; j <= n; j++) { 575 result |= d(i,j); 576 } 577 } 578 579 return result; 580 } 581 582 583 // Documentation see perl wrapper 584 template <typename Addition> 585 BigObject rational_curve_from_matroid_coordinates(Vector<Rational> matroidVector) 586 { 587 matroidVector = matroidVector.slice(range_from(1)); 588 589 // Convert vector to a map 590 Int n = moduliDimensionFromLength(matroidVector.dim())+1; 591 Matrix<Rational> d(n, n); 592 Int index = 0; 593 for (Int i = 1; i < n-1; ++i) { 594 for (Int j = i+1; j <= n-1; ++j) { 595 // The isomorphism is rigged for max, so we need to insert a sign here 596 d(i,j) = (-Addition::orientation())*matroidVector[index]; 597 ++index; 598 } 599 } 600 601 // Now apply mapping 602 Vector<Rational> metric; 603 for (Int i = 1; i < n; ++i) { 604 for (Int j = i+1; j <= n; ++j) { 605 if (j == n) { 606 metric |= 0; 607 } else { 608 metric |= (2* d(i,j)); 609 } 610 } 611 } 612 613 return curveFromMetric(metric); 614 } 615 616 // Documentation see perl wrapper 617 template <typename Addition> 618 ListReturn rational_curve_list_from_matroid_coordinates(const Matrix<Rational>& m) 619 { 620 ListReturn result; 621 622 for (Int i = 0; i < m.rows(); ++i) { 623 result << rational_curve_from_matroid_coordinates<Addition>(m.row(i)); 624 } 625 626 return result; 627 } 628 629 630 /** 631 @brief Takes a rational n-marked abstract curve and computes its representation in the matroid coordinates of the moduli space 632 @param BigObject The rational curve 633 @return Vector<Rational> 634 */ 635 template <typename Addition> 636 Vector<Rational> matroid_coordinates_from_curve(BigObject curve) 637 { 638 // Extract values 639 IncidenceMatrix<> sets = curve.give("SETS"); 640 Vector<Rational> coeffs = curve.give("COEFFS"); 641 const Int n = curve.give("N_LEAVES"); 642 643 // Create edge index map (i,j) -> vector index 644 Matrix<Int> E(n, n); 645 Int index = 0; 646 for (Int i = 1; i < n-1; ++i) { 647 for (Int j = i+1; j <= n-1; ++j) { 648 E(i,j) = E(j,i) = index; 649 ++index; 650 } 651 } 652 653 // Compute ambient dimension of moduli space 654 const Int raydim = (n*(n-3))/2 +1; 655 const auto completeSet = sequence(1,n); 656 657 Vector<Rational> result(raydim); 658 659 // Map each set to matroid coordinates with appropriate coefficient 660 for (Int s = 0; s < sets.rows(); ++s) { 661 Set<Int> sset = sets.row(s); 662 // Make sure the set does not contain n 663 if (sset.contains(n)) sset = completeSet - sset; 664 // Now create the flat vector for the complete graph on vertices in sset 665 Vector<Int> slist(sset); 666 for (Int i = 0; i < slist.dim(); ++i) { 667 for (Int j = i+1; j < slist.dim(); ++j) { 668 result[E(slist[i],slist[j])] += Addition::orientation() * coeffs[s]; 669 } 670 } 671 } 672 673 result = (Rational(0) | result); 674 return result; 675 } 676 677 678 // Documentation see perl wrapper 679 ListReturn curveFromMetricMatrix(const Matrix<Rational>& m) 680 { 681 ListReturn result; 682 683 for (Int i = 0; i < m.rows(); ++i) { 684 result << curveFromMetric(m.row(i)); 685 } 686 687 return result; 688 } 689 690 691 /** 692 @brief This computes the properties [[NODES_BY_SETS]] and [[NODES_BY_LEAVES]] of a RationalCurve object 693 */ 694 void computeNodeData(BigObject curve) 695 { 696 Vector<Rational> metric = curve.call_method("metric_vector"); 697 // We recompute the curve to make sure the graph edges have the same order 698 // as the curve sets 699 BigObject newcurve = curveAndGraphFromMetric(metric); 700 701 // We might have to permute the column indices in the node matrices, since the sets might 702 // be in a different order in the actual curve 703 // For this we have to normalize both set descriptions to contain the element 1 704 705 const Int n = newcurve.give("N_LEAVES"); 706 const auto all_leaves = sequence(1, n); 707 IncidenceMatrix<> newsetsInc = newcurve.give("SETS"); 708 Vector<Set<Int>> newsets = incMatrixToVector(newsetsInc); 709 for (Int ns = 0; ns < newsets.dim(); ++ns) { 710 if (*(newsets[ns].begin()) != 1) 711 newsets[ns] = all_leaves - newsets[ns]; 712 } 713 IncidenceMatrix<> oldsetsInc = curve.give("SETS"); 714 Vector<Set<Int>> oldsets = incMatrixToVector(oldsetsInc); 715 for (Int os = 0; os < oldsets.dim(); ++os) { 716 if(*(oldsets[os].begin()) != 1) 717 oldsets[os] = all_leaves - oldsets[os]; 718 } 719 Array<Int> perm(newsets.dim()); 720 for (Int i = 0; i < perm.size(); ++i) { 721 // Find equal set 722 for (Int j = 0; j < perm.size(); ++j) { 723 if (newsets[i] == oldsets[j]) { 724 perm[i] = j; break; 725 } 726 } 727 } 728 729 IncidenceMatrix<> new_node_sets = newcurve.give("NODES_BY_SETS"); 730 IncidenceMatrix<> node_leaves = newcurve.give("NODES_BY_LEAVES"); 731 Vector<Int> node_degrees = newcurve.give("NODE_DEGREES"); 732 733 // Convert the node set matrix 734 Vector<Set<Int>> old_node_sets; 735 for (Int nns = 0; nns < new_node_sets.rows(); ++nns) { 736 Set<Int> new_edge = new_node_sets.row(nns); 737 Set<Int> old_edge; 738 for (auto ne = entire(new_edge); !ne.at_end(); ++ne) { 739 old_edge += perm[*ne]; 740 } 741 old_node_sets |= old_edge; 742 } 743 744 curve.take("NODES_BY_LEAVES") << node_leaves; 745 curve.take("NODES_BY_SETS") << old_node_sets; 746 curve.take("NODE_DEGREES") << node_degrees; 747 } 748 749 750 UserFunction4perl("# @category Abstract rational curves" 751 "# Takes a vector from Q^(n over 2) that describes an n-marked rational abstract" 752 "# curve as a distance vector between its leaves. It then computes the " 753 "# curve corresponding to this vector." 754 "# @param Vector<Rational> v A vector of length (n over 2). Its entries are " 755 "# interpreted as the distances d(i,j) ordered lexicographically according to i,j. However, they need not be positive, as long as v is equivalent to a proper " 756 "# metric modulo leaf lengths." 757 "# @return RationalCurve", 758 &curveFromMetric,"rational_curve_from_metric(Vector<Rational>)"); 759 760 UserFunctionTemplate4perl("# @category Abstract rational curves" 761 "# Takes a vector from $ Q^{(n-1) over 2} $ that lies in $ M_{0,n} $ (in its matroid coordinates) " 762 "# and computes the corresponding rational curve." 763 "# In the isomorphism of the metric curve space and the moduli coordinates" 764 "# the last leaf is considered as the special leaf" 765 "# @param Vector<Rational> v A vector in the moduli space (WITH leading coordinate)." 766 "# @tparam Addition Min or Max (i.e. what are the matroid coordinates using)" 767 "# @return RationalCurve", 768 "rational_curve_from_matroid_coordinates<Addition>(Vector<Rational>)"); 769 770 UserFunctionTemplate4perl("# @category Abstract rational curves" 771 "# Takes a matrix whose rows are elements in the moduli space M_0,n in matroid " 772 "# coordinates. Returns a list, where the i-th element is the curve corr. to " 773 "# the i-th row in the matrix" 774 "# @param Matrix<Rational> m A list of vectors in the moduli space (with leading coordinate)." 775 "# @tparam Addition Mir or Max (i.e. what are the matroid coordinates using" 776 "# @return RationalCurve : An array of RationalCurves", 777 "rational_curve_list_from_matroid_coordinates<Addition>(Matrix<Rational>)"); 778 779 UserFunction4perl("# @category Abstract rational curves" 780 "# Takes a matrix whose rows are metrics of rational n-marked curves." 781 "# Returns a list, where the i-th element is the curve corr. to " 782 "# the i-th row in the matrix" 783 "# @param Matrix<Rational> m" 784 "# @return RationalCurve : An array of RationalCurves", 785 &curveFromMetricMatrix, "rational_curve_list_from_metric(Matrix<Rational>)"); 786 787 UserFunction4perl("# @category Abstract rational curves" 788 "# Takes a metric vector in Q^{(n over 2)} and checks whether it fulfills " 789 "# the four-point condition, i.e. whether it lies in M_0,n. More precisely " 790 "# it only needs to be equivalent to such a vector" 791 "# @param Vector<Rational> v The vector to be checked" 792 "# @return Int A quadruple (array) of indices, where the four-point condition " 793 "# is violated or an empty list, if the vector is indeed in M_0,n", 794 &wrapTestFourPointCondition, "testFourPointCondition(Vector<Rational>)"); 795 796 UserFunctionTemplate4perl("# @category Abstract rational curves" 797 "# Takes a rational curve and converts it into the corresponding matroid coordinates" 798 "# in the moduli space of rational curves (including the leading 0 for a ray!)" 799 "# @param RationalCurve r A rational n-marked curve" 800 "# @tparam Addition Min or Max, i.e. which coordinates to use." 801 "# @return Vector<Rational> The matroid coordinates, tropically homogeneous and" 802 "# with leading coordinate", 803 "matroid_coordinates_from_curve<Addition>(RationalCurve)"); 804 805 Function4perl(&graphFromMetric, "curve_graph_from_metric(Vector)"); 806 Function4perl(&metricFromCurve, "metric_from_curve(IncidenceMatrix, Vector<Rational>, $)"); 807 Function4perl(&computeNodeData, "compute_node_data(RationalCurve)"); 808 809 } } 810