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