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/fan/stacky_fundamental_domain.h"
20 #include "polymake/topaz/hasse_diagram.h"
21 #include "polymake/graph/LatticePermutation.h"
22
23 namespace polymake { namespace fan {
24
25 using graph::Lattice;
26 using graph::lattice::BasicDecoration;
27 using graph::lattice::Sequential;
28 using graph::lattice::Nonsequential;
29
30
31 template<typename Scalar>
32 Set<Int>
orbit_rep(const Matrix<Scalar> & subdiv_rays,const hash_set<Array<Int>> & entire_group,const Map<Vector<Scalar>,Int> & index_of_ray_orbit_member,const Set<Int> & face)33 orbit_rep(const Matrix<Scalar>& subdiv_rays,
34 const hash_set<Array<Int>>& entire_group,
35 const Map<Vector<Scalar>, Int>& index_of_ray_orbit_member,
36 const Set<Int>& face)
37 {
38 Set<Set<Int>> the_orbit;
39 Set<Int> image;
40 for (const Array<Int>& homog_coo_perm: entire_group) {
41 pm::operations::group::action<Vector<Scalar>, pm::operations::group::on_container, Array<Int>> this_action(homog_coo_perm);
42 image.clear();
43 for (const Int i: face)
44 image += index_of_ray_orbit_member[this_action(subdiv_rays[i])];
45 the_orbit += image;
46 }
47 return the_orbit.front();
48 }
49
50 template<typename Scalar>
51 std::vector<Int>
find_facet_rep_indices(const Array<Set<Int>> & SD_facets,const Matrix<Scalar> & subdiv_rays,const Map<Vector<Scalar>,Int> & index_of_ray_orbit_member,const hash_set<Array<Int>> & entire_group,const Graph<> & dual_graph,const Int verbosity)52 find_facet_rep_indices(const Array<Set<Int>>& SD_facets,
53 const Matrix<Scalar>& subdiv_rays,
54 const Map<Vector<Scalar>, Int>& index_of_ray_orbit_member,
55 const hash_set<Array<Int>>& entire_group,
56 const Graph<>& dual_graph,
57 const Int verbosity)
58 {
59 Set<Int> seen_indices {0};
60 std::list<Int> queue {0};
61 std::vector<Int> accepted_indices;
62 Set<Set<Int>> facet_reps;
63
64 while (queue.size()) {
65 if (facet_reps.size() != convert_to<Int>(accepted_indices.size())) {
66 cerr << "problem: facet_reps (" << facet_reps.size() << "):\n" << facet_reps << endl
67 << "accepted_indices (" << accepted_indices.size() << "):\n" << accepted_indices << endl;
68 throw std::runtime_error("stop");
69 }
70 const Int current_index(queue.front()); queue.pop_front();
71 const Set<Int> rep = orbit_rep(subdiv_rays, entire_group, index_of_ray_orbit_member, SD_facets[current_index]);
72
73 if (verbosity>2)
74 cerr << "\nqueue now " << queue << ", current_index: " << current_index << ": " << SD_facets[current_index]
75 << " -> " << rep << endl;
76
77 if (facet_reps.collect(rep))
78 continue;
79
80 accepted_indices.push_back(current_index);
81 if (verbosity > 2)
82 cerr << "accepted " << current_index << " = " << SD_facets[current_index] << endl;
83
84 for (auto nb_iter = entire(dual_graph.adjacent_nodes(current_index)); !nb_iter.at_end(); ++nb_iter)
85 if (!seen_indices.contains(*nb_iter)) {
86 queue.push_back(*nb_iter);
87 seen_indices += *nb_iter;
88
89 if (verbosity > 2)
90 cerr << "added neighbor " << *nb_iter << ": " << SD_facets[*nb_iter] << endl;
91 }
92 }
93 if (verbosity > 2)
94 cerr << "done with queue" << endl << endl;
95
96 return accepted_indices;
97 }
98
99 template<typename LabelType, typename Scalar>
100 struct FacetsLabelsCoordinates {
101 FacetsLabelsReordering<LabelType> flr;
102 Matrix<Scalar> coordinates;
103 Matrix<Scalar> used_original_coordinates;
104 };
105
106 template<typename Scalar, typename FacetType, typename LabelType>
107 FacetsLabelsCoordinates<LabelType, Scalar>
squeeze_data(const FacetType & facets,const LabelType & labels,const Matrix<Scalar> & coordinates,const Int verbosity)108 squeeze_data(const FacetType& facets,
109 const LabelType& labels,
110 const Matrix<Scalar>& coordinates,
111 const Int verbosity)
112 {
113 FacetsLabelsCoordinates<LabelType, Scalar> squeezed_flc;
114 squeezed_flc.flr = squeeze_facets_and_labels(facets, labels, verbosity);
115 squeezed_flc.coordinates = coordinates.minor(squeezed_flc.flr.reordering, All);
116
117 Set<Int> used_original_labels;
118 for (const auto& ssi: squeezed_flc.flr.labels)
119 used_original_labels += ssi.front();
120
121 squeezed_flc.used_original_coordinates = coordinates.minor(used_original_labels, All);
122 return squeezed_flc;
123 }
124
125 template<typename Scalar>
126 void
paranoid_consistency_test(const Array<Set<Int>> & SD_facets,const Matrix<Scalar> & subdiv_rays,const Map<Vector<Scalar>,Int> & index_of_ray_orbit_member,const hash_set<Array<Int>> & entire_group,const std::vector<Int> & accepted_indices)127 paranoid_consistency_test(const Array<Set<Int>>& SD_facets,
128 const Matrix<Scalar>& subdiv_rays,
129 const Map<Vector<Scalar>, Int>& index_of_ray_orbit_member,
130 const hash_set<Array<Int>>& entire_group,
131 const std::vector<Int>& accepted_indices)
132 {
133 cerr << "paranoid_consistency_test" << endl;
134 Set<Set<Int>> accepted_reps;
135 for (const Int i: accepted_indices)
136 accepted_reps += orbit_rep(subdiv_rays, entire_group, index_of_ray_orbit_member, SD_facets[i]);
137 if (accepted_reps.size() != convert_to<Int>(accepted_indices.size())) {
138 cerr << "problem: accepted_indices(" << accepted_indices.size() << "):\n" << accepted_indices << endl
139 << "accepted_reps(" << accepted_reps.size() << "):\n" << accepted_reps << endl << endl;
140 throw std::runtime_error("stop");
141 }
142 cerr << "Each rep represents one orbit" << endl;
143
144 Set<Set<Int>> all_reps;
145 for (const auto& f: SD_facets)
146 all_reps += orbit_rep(subdiv_rays, entire_group, index_of_ray_orbit_member, f);
147 if (all_reps != accepted_reps) {
148 cerr << "problem: all_reps(" << all_reps.size() << "):\n" << all_reps << endl
149 << "accepted_reps(" << accepted_reps.size() << "):\n" << accepted_reps << endl << endl;
150 throw std::runtime_error("stop");
151 }
152 cerr << "Each original simplex has a representative" << endl;
153
154 Map<Set<Int>,Int> index_of_facet_image;
155 for (auto fit = entire<indexed>(SD_facets); !fit.at_end(); ++fit)
156 index_of_facet_image[*fit] = fit.index();
157
158
159 Map<Vector<Scalar>, Int> index_of_ray;
160 for (auto rit = entire<indexed>(rows(subdiv_rays)); !rit.at_end(); ++rit)
161 index_of_ray[Vector<Scalar>(*rit)] = rit.index();
162
163 Set<Int> seen_indices;
164 std::vector<Set<Int>> image_indices_store;
165 for (Int i: accepted_indices) {
166 Set<Int> image_indices;
167 Set<Set<Int>> image_simplices;
168 Set<Int> image_simplex;
169 for (const auto& g: entire_group) {
170 pm::operations::group::action<Vector<Scalar>, pm::operations::group::on_container, Array<Int>> this_action(g);
171 image_simplex.clear();
172 for (const Int j: SD_facets[i]) {
173 const Vector<Scalar> ray_image(this_action(subdiv_rays[j]));
174 if (!index_of_ray.contains(ray_image)) {
175 const Int next_index(index_of_ray.size());
176 index_of_ray[ray_image] = next_index;
177 }
178 image_simplex += index_of_ray[ray_image];
179 }
180 if (!index_of_facet_image.contains(image_simplex)) {
181 const Int next_index(index_of_facet_image.size());
182 index_of_facet_image[image_simplex] = next_index;
183 }
184 image_indices += index_of_facet_image[image_simplex];
185 image_simplices += image_simplex;
186 }
187 seen_indices += image_indices;
188 image_indices_store.push_back(image_indices);
189 }
190
191 if (incl(Set<Int>(sequence(0, SD_facets.size())), seen_indices) > 0) {
192 cerr << "problem: have not exhausted all simplices:\n" << seen_indices << endl;
193 throw std::runtime_error("stop");
194 }
195
196 cerr << "reps represent all simplices." << endl;
197
198 for (std::size_t i=0; i<image_indices_store.size() - 1; ++i)
199 for (std::size_t j=i+1; j<image_indices_store.size(); ++j)
200 if ((image_indices_store[i] * image_indices_store[j]).size()) {
201 cerr << "...but some simplices are not uniquely represented:" << endl
202 << i << ": " << image_indices_store[i] << endl
203 << j << ": " << image_indices_store[j] << endl << endl
204 << "store:\n";
205 for (std::size_t k=0; k <= std::max(i,j); ++k)
206 cerr << k << ": " << image_indices_store[k] << endl;
207 throw std::runtime_error("stop");
208 }
209
210 cerr << "test passed. size of fundamental domain: " << accepted_indices.size() << endl << endl;
211 }
212
213 template<typename Scalar>
214 BigObject
stacky_fundamental_domain(BigObject StackyFan,OptionSet options)215 stacky_fundamental_domain(BigObject StackyFan, OptionSet options)
216 {
217 const Int verbosity = options["verbosity"];
218 if (verbosity)
219 cerr << "\n*** in stacky_fundamental_domain ***" << endl;
220
221 const Matrix<Scalar> generating_rays = StackyFan.give("GENERATING_RAYS");
222 if (verbosity > 2) {
223 cerr << "generating_rays:\n";
224 for (Int i=0; i<generating_rays.rows(); ++i)
225 cerr << i << ": " << generating_rays[i] << endl;
226 cerr << endl << endl;
227 }
228
229 const bool has_trivial_group(!StackyFan.exists("GROUP"));
230 if (has_trivial_group && verbosity > 2)
231 cerr << "trivial automorphism group" << endl << endl;
232
233 const Lattice<BasicDecoration, Sequential> HD = StackyFan.give("GENERATING_HASSE_DIAGRAM");
234
235 const auto facets_and_labels = topaz::first_barycentric_subdivision(HD);
236 const auto& SD_facets(facets_and_labels.facets);
237 const auto& SD_labels(facets_and_labels.labels);
238
239 if (verbosity > 2) {
240 cerr << "SD_labels (" << SD_labels.size() << "):\n";
241 for (Int i=0; i<SD_labels.size(); ++i)
242 cerr << i << ": " << SD_labels[i] << endl;
243 cerr << endl << endl
244 << "SD_facets (" << SD_facets.size() << "):\n" << SD_facets << endl << endl;
245 }
246
247 Matrix<Scalar> subdiv_rays(SD_labels.size(), generating_rays.cols());
248 for (Int i=0; i<SD_labels.size(); ++i)
249 subdiv_rays[i] = accumulate(rows(generating_rays.minor(SD_labels[i].front(), All)), operations::add());
250 if (verbosity > 2) {
251 cerr << "subdiv_rays:\n";
252 for (Int i=0; i<subdiv_rays.rows(); ++i)
253 cerr << i << ": " << subdiv_rays[i] << endl;
254 cerr << endl << endl;
255 }
256
257 if (has_trivial_group) {
258 BigObject fd("topaz::GeometricSimplicialComplex", mlist<Scalar>(),
259 "COORDINATES", subdiv_rays,
260 "FACETS", SD_facets);
261 fd.attach("IS_FUNDAMENTAL_DOMAIN") << bool(true);
262 fd.attach("VERTEX_LABELS_AS_SETS") << SD_labels;
263 fd.attach("USED_ORIGINAL_COORDINATES") << subdiv_rays;
264
265 return fd;
266 }
267
268 const Array<Array<Int>> homog_action_generators = StackyFan.give("GROUP.HOMOGENEOUS_COORDINATE_ACTION.GENERATORS");
269 if (verbosity > 3)
270 cerr << "action generators:\n" << homog_action_generators << endl;
271
272 Int max_index(0);
273 Map<Vector<Scalar>, Int> index_of_ray_orbit_member;
274 for (const auto& v: rows(subdiv_rays)) {
275 const auto the_orbit(group::unordered_orbit<pm::operations::group::on_container,
276 Array<Int>, Vector<Scalar>>(homog_action_generators, Vector<Scalar>(v)));
277
278 // if one element of the orbit has already been indexed, go to the next orbit
279 if (index_of_ray_orbit_member.exists(*(the_orbit.begin())))
280 continue;
281 for (const auto& member: the_orbit)
282 if (!index_of_ray_orbit_member.exists(member))
283 index_of_ray_orbit_member[member] = max_index++;
284 }
285
286 if (verbosity > 2)
287 cerr << "\nindex_of_ray_orbit_member (" << index_of_ray_orbit_member.size() << "):\n" << index_of_ray_orbit_member << endl << endl;
288
289 const hash_set<Array<Int>> entire_group = group::unordered_orbit<pm::operations::group::on_container,
290 Array<Int>, Array<Int>>(homog_action_generators, Array<Int>(sequence(0,homog_action_generators[0].size())));
291 if (verbosity)
292 cerr << "group size: " << entire_group.size() << endl;
293 if (verbosity > 2)
294 cerr << "entire_group:\n" << entire_group << endl << endl;
295
296 if (verbosity)
297 cerr << "calculating dual graph of complex with " << SD_facets.size() << " facets and " << SD_labels.size() << " vertices" << endl;
298
299 BigObject SC("topaz::SimplicialComplex",
300 "FACETS", SD_facets);
301 const Graph<> dual_graph = SC.give("DUAL_GRAPH.ADJACENCY");
302 if (verbosity > 2)
303 cerr << "dual graph:\n" << dual_graph << endl << endl;
304
305 const std::vector<Int> facet_rep_indices = find_facet_rep_indices(SD_facets, subdiv_rays, index_of_ray_orbit_member, entire_group, dual_graph, verbosity);
306 if (verbosity)
307 cerr << "fundamental domain in first barycentric subdivision has "
308 << facet_rep_indices.size() << " simplices out of " << SD_facets.size() << endl;
309 if (verbosity > 2)
310 cerr << "accepted facet_rep_indices:\n" << facet_rep_indices << endl << endl
311 << "corresponding to facets\n" << select(SD_facets, facet_rep_indices) << endl << endl;
312
313 if (verbosity > 2)
314 paranoid_consistency_test(SD_facets, subdiv_rays, index_of_ray_orbit_member, entire_group, facet_rep_indices);
315
316 const auto squeezed_flc = squeeze_data(select(SD_facets, facet_rep_indices), SD_labels, subdiv_rays, verbosity);
317 if (verbosity > 2) {
318 cerr << "squeezed facets:\n" << squeezed_flc.flr.facets
319 << "\n\nsqueezed labels:\n";
320 for (auto it = entire<indexed>(squeezed_flc.flr.labels); !it.at_end(); ++it)
321 cerr << it.index() << ": " << *it << endl;
322
323 cerr << "\n\nsqueezed coordinates:\n" << squeezed_flc.coordinates
324 << "\n\nused original coordinates:\n" << squeezed_flc.used_original_coordinates << endl << endl;
325 }
326
327 BigObject hc_a("group::PermutationAction", "GENERATORS", homog_action_generators);
328 BigObject g("group::Group", "Aut",
329 "HOMOGENEOUS_COORDINATE_ACTION", hc_a);
330
331
332 BigObject fd("topaz::GeometricSimplicialComplex", mlist<Scalar>(),
333 "COORDINATES", squeezed_flc.coordinates,
334 "FACETS", squeezed_flc.flr.facets,
335 "GROUP", g);
336 fd.attach("IS_FUNDAMENTAL_DOMAIN") << bool(true);
337 fd.attach("VERTEX_LABELS_AS_SETS") << squeezed_flc.flr.labels;
338 fd.attach("USED_ORIGINAL_COORDINATES") << squeezed_flc.used_original_coordinates;
339 return fd;
340 }
341
342 UserFunctionTemplate4perl("# @category Symmetry"
343 "# Find a fundamental domain for a cone modulo the action of a symmetry group."
344 "# The fundamental domain will be a subcomplex, with connected DUAL_GRAPH,"
345 "# of the first barycentric subdivision that is found via a breadth-first search."
346 "# @param DisjointStackyFan F"
347 "# @return topaz::GeometricSimplicialComplex",
348 "stacky_fundamental_domain<Scalar>(DisjointStackyFan<Scalar>, { verbosity=>0 })");
349
350 } }
351
352 // Local Variables:
353 // mode:C++
354 // c-basic-offset:3
355 // indent-tabs-mode:nil
356 // End:
357
358