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/Array.h"
20 #include "polymake/Rational.h"
21 #include "polymake/Set.h"
22 #include "polymake/IncidenceMatrix.h"
23 #include "polymake/Matrix.h"
24 #include "polymake/ListMatrix.h"
25 #include "polymake/TropicalNumber.h"
26 #include "polymake/linalg.h"
27 #include "polymake/fan/tight_span.h"
28 #include "polymake/graph/lattice_builder.h"
29 
30 namespace polymake { namespace tropical {
31 
32 using namespace graph;
33 using namespace graph::lattice;
34 using namespace fan;
35 
36 template <typename Addition>
linear_space(BigObject valuated_matroid)37 BigObject linear_space(BigObject valuated_matroid)
38 {
39   const BigObject polytope = valuated_matroid.give("POLYTOPE");
40   const Matrix<Rational> &vertices = polytope.give("VERTICES");
41   const auto no_front_set = sequence(1,vertices.cols()-1);
42   const auto vertices_no_front = vertices.minor(All,no_front_set);
43   const Int n = valuated_matroid.give("N_ELEMENTS");
44   Int n_facets = valuated_matroid.give("N_BASES");
45   const Vector<TropicalNumber<Addition> > &valuation = valuated_matroid.give("VALUATION_ON_BASES");
46   const Vector<Rational> rational_valuation(valuation);
47   const Array<Set<Int>>& subdivision = valuated_matroid.give("SUBDIVISION");
48   const Array<Array<Set<Int>>>& split_flacets = valuated_matroid.give("SPLIT_FLACETS");
49   const Int polytope_dim = polytope.call_method("DIM");
50   ListMatrix<Vector<Rational> > new_vertices;
51 
52   // Check absence of loops
53   const Int n_matroid_loops = valuated_matroid.give("N_LOOPS");
54   if (n_matroid_loops > 0) {
55     // an empty cycle
56     return BigObject("Cycle", mlist<Addition>(),
57                      "PROJECTIVE_VERTICES", Matrix<Rational>(0, n+1),
58                      "MAXIMAL_POLYTOPES", Array<Set<Int>>(),
59                      "PROJECTIVE_AMBIENT_DIM", n-1,
60                      "WEIGHTS", Vector<Integer>());
61   }
62 
63   // excluded faces (those with loops):
64   Array<Set<Int>> including_bases(n);
65   std::list<Set<Int>> non_including_bases(n);
66   const auto total_list = sequence(0,n_facets);
67   auto non_bases_it = entire(non_including_bases);
68   auto bases_it = entire(including_bases);
69   for (auto vcol = entire(cols(vertices_no_front)); !vcol.at_end();
70        ++vcol, ++bases_it, ++non_bases_it) {
71     *bases_it = support(*vcol);
72     *non_bases_it = total_list - *bases_it;
73   }
74 
75   // Vertices of the tight span (for each max. cell in the subdivision):
76   const auto& equations = -rational_valuation | vertices_no_front;
77   for (const auto& cell : subdivision) {
78     auto nspace = null_space( equations.minor(cell,All));
79     auto ncol = entire(nspace.col(0));
80     for (auto nrows = entire(rows(nspace)); !nrows.at_end(); ++nrows, ++ncol) {
81       if (!ncol->is_zero()) {
82         new_vertices /= *nrows / *ncol;
83         break;
84       }
85     }
86   }
87 
88   // stack the 'loop-free' facets and add a ray for each such facet
89   RestrictedIncidenceMatrix<> flacets(subdivision.size(), rowwise(), entire(subdivision));
90   Int i = 1;
91   for (auto incl_base  = entire(including_bases); !incl_base.at_end(); ++incl_base, ++i) {
92     const Int n_loops = attach_selector( cols(vertices_no_front.minor(*incl_base,All)), operations::is_zero()).size();
93     if (n_loops > 0 || rank( vertices.minor(*incl_base,All)) != polytope_dim) continue;
94 
95     new_vertices /= (Addition::orientation() * unit_vector<Rational>(n+1,i));
96     flacets /= ( (*incl_base) + n_facets);
97     n_facets++;
98   }
99   for (auto rk = entire<indexed>(split_flacets); !rk.at_end(); ++rk) {
100     for (const auto& flacet : *rk) {
101       Vector<Rational> v(n);
102       v.slice(flacet).fill(1);
103       v = -rk.index() | v;
104       auto col_sum_supp = total_list - support(vertices * v);
105       if (rank( vertices.minor (col_sum_supp,All)) != polytope_dim) continue;
106 
107       v[0] = 0;
108       flacets /= col_sum_supp + n_facets;
109       new_vertices /= Addition::orientation() * v;
110       ++n_facets;
111     }
112   }
113 
114   // Build Hasse diagram
115   const IncidenceMatrix<> flacet_inc(std::move(flacets));
116   NoBoundaryCut cut( non_including_bases, flacet_inc);
117   BasicClosureOperator<> cop(flacet_inc.rows(), T(flacet_inc));
118   BasicDecorator<> dec(0, scalar2set(-1));
119   Lattice<BasicDecoration> hasse_diagram = lattice_builder::compute_lattice_from_closure<BasicDecoration>(
120      cop, cut, dec, 1, lattice_builder::Primal());
121   const Int n_max_polys = hasse_diagram.in_adjacent_nodes(hasse_diagram.top_node()).size();
122 
123   // Build lineality space
124   const Array<Set<Int>>& ccomp = valuated_matroid.give("CONNECTED_COMPONENTS");
125   Matrix<Rational> lin_space(ccomp.size()-1, n+1);
126 
127   auto lin_no_front = sequence(1, lin_space.cols()-1);
128   auto lin_space_no_front = lin_space.minor(All,lin_no_front);
129   auto lin_rows = entire(rows(lin_space_no_front));
130   auto cc = entire(ccomp);
131   if (!cc.at_end()) {
132     // We take all connected components but the first.
133     while (!(++cc).at_end()) {
134       lin_rows->slice(*cc).fill(1);
135       ++lin_rows;
136     }
137   }
138 
139   return BigObject("Cycle", mlist<Addition>(),
140                    "PROJECTIVE_VERTICES", new_vertices,
141                    "HASSE_DIAGRAM", hasse_diagram,
142                    "LINEALITY_SPACE", lin_space,
143                    "WEIGHTS", ones_vector<Integer>(n_max_polys));
144 }
145 
146 UserFunctionTemplate4perl("# @category Tropical linear spaces"
147                           "# This computes the tropical linear space (with the coarsest structure) associated to a valuated matroid."
148                           "# If you have a trivial valuation, it is highly recommended, you use"
149                           "# [[matroid_fan]] instead."
150                           "# @param matroid::ValuatedMatroid<Addition,Rational> A valuated matroid, whose value group must be the rationals."
151                           "# @return Cycle<Addition>",
152                           "linear_space<Addition>(matroid::ValuatedMatroid<Addition>)");
153 } }
154