1 /*
2 * Normaliz
3 * Copyright (C) 2007-2021 W. Bruns, B. Ichim, Ch. Soeger, U. v. d. Ohe
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * As an exception, when this program is distributed through (i) the App Store
18 * by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or (iii) Google Play
19 * by Google Inc., then that store may impose any digital rights management,
20 * device limits and/or redistribution restrictions that are required by its
21 * terms of service.
22 */
23
24 //---------------------------------------------------------------------------
25
26 #include <algorithm>
27 #include <string>
28 #include <iostream>
29 #include <set>
30 #include <deque>
31 #include <csignal>
32
33 #include <ctime>
34
35 #include "libnormaliz/integer.h"
36 #include "libnormaliz/vector_operations.h"
37 #include "libnormaliz/matrix.h"
38 #include "libnormaliz/simplex.h"
39 #include "libnormaliz/list_and_map_operations.h"
40 #include "libnormaliz/HilbertSeries.h"
41 #include "libnormaliz/cone.h"
42 // #include "libnormaliz/bottom.h"
43
44 //---------------------------------------------------------------------------
45
46 namespace libnormaliz {
47 using namespace std;
48
49 //---------------------------------------------------------------------------
50 // Subdivision of large simplices
51 //---------------------------------------------------------------------------
52
53 long SubDivBound = 1000000;
54
55 template <typename Integer>
56 bool bottom_points_inner(Matrix<Integer>& gens,
57 list<vector<Integer> >& local_new_points,
58 vector<Matrix<Integer> >& local_q_gens,
59 size_t& stellar_det_sum);
60
61 template <typename Integer>
bottom_points(list<vector<Integer>> & new_points,const Matrix<Integer> & given_gens,Integer VolumeBound)62 void bottom_points(list<vector<Integer> >& new_points, const Matrix<Integer>& given_gens, Integer VolumeBound) {
63 /* gens.pretty_print(cout);
64 cout << "=======================" << endl;
65
66 gens.transpose().pretty_print(cout);
67 cout << "=======================" << endl;*/
68
69 Matrix<Integer> gens, Trans, Trans_inv;
70 // given_gens.LLL_transform_transpose(gens,Trans,Trans_inv); // now in optimal_subdivision_point()
71 gens = given_gens;
72
73 Integer volume;
74 // int dim = gens[0].size();
75 Matrix<Integer> Support_Hyperplanes = gens.invert(volume);
76
77 vector<Integer> grading; // = grading_;
78 if (grading.empty())
79 grading = gens.find_linear_form();
80 // cout << grading;
81
82 list<vector<Integer> > bottom_candidates;
83 bottom_candidates.splice(bottom_candidates.begin(), new_points);
84 // Matrix<Integer>(bottom_candidates).pretty_print(cout);
85
86 if (verbose) {
87 verboseOutput() << "Computing bbottom points using projection " << endl;
88 }
89
90 if (verbose) {
91 verboseOutput() << "simplex volume " << volume << endl;
92 }
93
94 //---------------------------- begin stellar subdivision -------------------
95
96 size_t stellar_det_sum = 0;
97 vector<Matrix<Integer> > q_gens; // for successive stellar subdivision
98 q_gens.push_back(gens);
99 int level = 0; // level of subdivision
100
101 std::exception_ptr tmp_exception;
102 bool skip_remaining = false;
103 #pragma omp parallel // reduction(+:stellar_det_sum)
104 {
105 try {
106 vector<Matrix<Integer> > local_q_gens;
107 list<vector<Integer> > local_new_points;
108
109 while (!q_gens.empty()) {
110 if (skip_remaining)
111 break;
112 if (verbose) {
113 #pragma omp single
114 verboseOutput() << q_gens.size() << " simplices on level " << level++ << endl;
115 }
116
117 #pragma omp for schedule(static)
118 for (size_t i = 0; i < q_gens.size(); ++i) {
119 if (skip_remaining)
120 continue;
121
122 try {
123 bottom_points_inner(q_gens[i], local_new_points, local_q_gens, stellar_det_sum);
124 } catch (const std::exception&) {
125 tmp_exception = std::current_exception();
126 skip_remaining = true;
127 #pragma omp flush(skip_remaining)
128 }
129 }
130
131 #pragma omp single
132 { q_gens.clear(); }
133 #pragma omp critical(LOCALQGENS)
134 { q_gens.insert(q_gens.end(), local_q_gens.begin(), local_q_gens.end()); }
135 local_q_gens.clear();
136 #pragma omp barrier
137 }
138
139 #pragma omp critical(LOCALNEWPOINTS)
140 { new_points.splice(new_points.end(), local_new_points, local_new_points.begin(), local_new_points.end()); }
141
142 } catch (const std::exception&) {
143 tmp_exception = std::current_exception();
144 skip_remaining = true;
145 #pragma omp flush(skip_remaining)
146 }
147
148 } // end parallel
149
150 //---------------------------- end stellar subdivision -----------------------
151
152 if (!(tmp_exception == 0))
153 std::rethrow_exception(tmp_exception);
154
155 // cout << new_points.size() << " new points accumulated" << endl;
156 new_points.sort();
157 new_points.unique();
158 if (verbose) {
159 verboseOutput() << new_points.size() << " bottom points accumulated in total." << endl;
160 verboseOutput() << "The sum of determinants of the stellar subdivision is " << stellar_det_sum << endl;
161 }
162
163 /* for(auto& it : new_points)
164 it=Trans_inv.VxM(it); */
165 }
166
167 //-----------------------------------------------------------------------------------------
168
169 template <typename Integer>
bottom_points_inner(Matrix<Integer> & gens,list<vector<Integer>> & local_new_points,vector<Matrix<Integer>> & local_q_gens,size_t & stellar_det_sum)170 bool bottom_points_inner(Matrix<Integer>& gens,
171 list<vector<Integer> >& local_new_points,
172 vector<Matrix<Integer> >& local_q_gens,
173 size_t& stellar_det_sum) {
174 INTERRUPT_COMPUTATION_BY_EXCEPTION
175
176 vector<Integer> grading = gens.find_linear_form();
177 Integer volume;
178 int dim = gens[0].size();
179 Matrix<Integer> Support_Hyperplanes = gens.invert(volume);
180
181 if (volume < SubDivBound) {
182 #pragma omp atomic
183 stellar_det_sum += convertToLongLong(volume);
184 return false; // not subdivided
185 }
186
187 // try st4ellar subdivision
188 Support_Hyperplanes = Support_Hyperplanes.transpose();
189 Support_Hyperplanes.make_prime();
190 vector<Integer> new_point;
191
192 if (new_point.empty()) {
193 // list<vector<Integer> > Dummy;
194 new_point = gens.optimal_subdivision_point(); // projection method
195 }
196
197 if (!new_point.empty()) {
198 // if (find(local_new_points.begin(), local_new_points.end(),new_point) == local_new_points.end())
199 local_new_points.emplace_back(new_point);
200 Matrix<Integer> stellar_gens(gens);
201
202 int nr_hyps = 0;
203 for (int i = 0; i < dim; ++i) {
204 if (v_scalar_product(Support_Hyperplanes[i], new_point) != 0) {
205 stellar_gens[i] = new_point;
206 local_q_gens.emplace_back(stellar_gens);
207
208 stellar_gens[i] = gens[i];
209 }
210 else
211 nr_hyps++;
212 }
213 //#pragma omp critical(VERBOSE)
214 // cout << new_point << " liegt in " << nr_hyps <<" hyperebenen" << endl;
215 return true; // subdivided
216 }
217 else { // could not subdivided
218 #pragma omp atomic
219 stellar_det_sum += convertToLongLong(volume);
220 return false;
221 }
222 }
223
224 // returns -1 if maximum is negative
225 template <typename Integer>
max_in_col(const Matrix<Integer> & M,size_t j)226 double max_in_col(const Matrix<Integer>& M, size_t j) {
227 Integer max = -1;
228 for (size_t i = 0; i < M.nr_of_rows(); ++i) {
229 if (M[i][j] > max)
230 max = M[i][j];
231 }
232 return convert_to_double(max);
233 }
234
235 // returns 1 if minimum is positive
236 template <typename Integer>
min_in_col(const Matrix<Integer> & M,size_t j)237 double min_in_col(const Matrix<Integer>& M, size_t j) {
238 Integer min = 1;
239 for (size_t i = 0; i < M.nr_of_rows(); ++i) {
240 if (M[i][j] < min)
241 min = M[i][j];
242 }
243 return convert_to_double(min);
244 }
245
246 #ifndef NMZ_MIC_OFFLOAD // offload with long is not supported
247 template void bottom_points(list<vector<long> >& new_points, const Matrix<long>& gens, long VolumeBound);
248 #endif // NMZ_MIC_OFFLOAD
249 template void bottom_points(list<vector<long long> >& new_points, const Matrix<long long>& gens, long long VolumeBound);
250 template void bottom_points(list<vector<mpz_class> >& new_points, const Matrix<mpz_class>& gens, mpz_class VolumeBound);
251
252 //---------------------------------------------------------------------------
253 // SimplexEvaluator
254 //---------------------------------------------------------------------------
255
256 template <typename Integer>
SimplexEvaluator(Full_Cone<Integer> & fc)257 SimplexEvaluator<Integer>::SimplexEvaluator(Full_Cone<Integer>& fc)
258 : C_ptr(&fc),
259 dim(fc.dim),
260 key(dim),
261 Generators(dim, dim),
262 LinSys(dim, 2 * dim + 1),
263 InvGenSelRows(dim, dim),
264 InvGenSelCols(dim, dim),
265 Sol(dim, dim + 1),
266 GDiag(dim),
267 TDiag(dim),
268 Excluded(dim),
269 Indicator(dim),
270 gen_degrees(dim),
271 gen_degrees_long(dim),
272 gen_levels(dim),
273 gen_levels_long(dim),
274 RS(dim, 1),
275 InExSimplData(C_ptr->InExCollect.size()),
276 RS_pointers(dim + 1),
277 unit_matrix(dim),
278 id_key(identity_key(dim)
279 // mpz_Generators(0,0)
280 ) {
281 if (fc.inhomogeneous)
282 ProjGen = Matrix<Integer>(dim - fc.level0_dim, dim - fc.level0_dim);
283
284 level0_gen_degrees.reserve(fc.dim);
285
286 for (size_t i = 0; i < fc.InExCollect.size(); ++i) {
287 InExSimplData[i].GenInFace.resize(fc.dim);
288 InExSimplData[i].gen_degrees.reserve(fc.dim);
289 }
290
291 sequential_evaluation = true; // to be changed later if necessrary
292 mpz_Generators = Matrix<mpz_class>(0, 0);
293 GMP_transition = false;
294 }
295
296 template <typename Integer>
set_evaluator_tn(int threadnum)297 void SimplexEvaluator<Integer>::set_evaluator_tn(int threadnum) {
298 tn = threadnum;
299 }
300
301 //---------------------------------------------------------------------------
302
303 template <typename Integer>
add_to_inex_faces(const vector<Integer> offset,size_t Deg,Collector<Integer> & Coll)304 void SimplexEvaluator<Integer>::add_to_inex_faces(const vector<Integer> offset, size_t Deg, Collector<Integer>& Coll) {
305 for (size_t i = 0; i < nrInExSimplData; ++i) {
306 bool in_face = true;
307 for (size_t j = 0; j < dim; ++j)
308 if ((offset[j] != 0) && !InExSimplData[i].GenInFace.test(j)) { // || Excluded[j] superfluous
309 in_face = false;
310 break;
311 }
312 if (!in_face)
313 continue;
314 Coll.InEx_hvector[i][Deg] += InExSimplData[i].mult;
315 }
316 }
317
318 //---------------------------------------------------------------------------
319
320 template <typename Integer>
prepare_inclusion_exclusion_simpl(size_t Deg,Collector<Integer> & Coll)321 void SimplexEvaluator<Integer>::prepare_inclusion_exclusion_simpl(size_t Deg, Collector<Integer>& Coll) {
322 Full_Cone<Integer>& C = *C_ptr;
323
324 nrInExSimplData = 0;
325
326 for (const auto& F : C.InExCollect) {
327 bool still_active = true;
328 for (size_t i = 0; i < dim; ++i)
329 if (Excluded[i] && !F.first.test(key[i])) {
330 still_active = false;
331 break;
332 }
333 if (!still_active)
334 continue;
335 InExSimplData[nrInExSimplData].GenInFace.reset();
336 for (size_t i = 0; i < dim; ++i)
337 if (F.first.test(key[i]))
338 InExSimplData[nrInExSimplData].GenInFace.set(i);
339 InExSimplData[nrInExSimplData].gen_degrees.clear();
340 for (size_t i = 0; i < dim; ++i)
341 if (InExSimplData[nrInExSimplData].GenInFace.test(i))
342 InExSimplData[nrInExSimplData].gen_degrees.push_back(gen_degrees_long[i]);
343 InExSimplData[nrInExSimplData].mult = F.second;
344 nrInExSimplData++;
345 }
346
347 if (C_ptr->do_h_vector) {
348 vector<Integer> ZeroV(dim, 0); // allowed since we have only kept faces that contain 0+offset
349 add_to_inex_faces(ZeroV, Deg, Coll); // nothing would change if we took 0+offset here
350 }
351 }
352
353 //---------------------------------------------------------------------------
354
355 template <typename Integer>
update_inhom_hvector(long level_offset,size_t Deg,Collector<Integer> & Coll)356 void SimplexEvaluator<Integer>::update_inhom_hvector(long level_offset, size_t Deg, Collector<Integer>& Coll) {
357 if (level_offset == 1) {
358 Coll.inhom_hvector[Deg]++;
359 return;
360 }
361
362 size_t Deg_i;
363
364 assert(level_offset == 0);
365
366 for (size_t i = 0; i < dim; ++i) {
367 if (gen_levels[i] == 1) {
368 Deg_i = Deg + gen_degrees_long[i];
369 Coll.inhom_hvector[Deg_i]++;
370 }
371 }
372 }
373
374 //---------------------------------------------------------------------------
375
376 // size_t Unimod=0, Ht1NonUni=0, Gcd1NonUni=0, NonDecided=0, NonDecidedHyp=0;
377
378 //---------------------------------------------------------------------------
379
380 template <typename Integer>
start_evaluation(SHORTSIMPLEX<Integer> & s,Collector<Integer> & Coll)381 Integer SimplexEvaluator<Integer>::start_evaluation(SHORTSIMPLEX<Integer>& s, Collector<Integer>& Coll) {
382 if (GMP_transition) {
383 mpz_Generators = Matrix<mpz_class>(0, 0); // this is not a local variable and must be deleted at the start
384 GMP_transition = false;
385 }
386
387 volume = s.vol;
388 key = s.key;
389 Full_Cone<Integer>& C = *C_ptr;
390 HB_bound_computed = false;
391
392 bool do_only_multiplicity = C.do_only_multiplicity;
393 // || (s.height==1 && C.do_partial_triangulation);
394
395 size_t i, j;
396
397 // degrees of the generators according to the Grading of C
398 if (C.isComputed(ConeProperty::Grading))
399 for (i = 0; i < dim; i++) {
400 if (!do_only_multiplicity || C.inhomogeneous || using_GMP<Integer>())
401 gen_degrees[i] = C.gen_degrees[key[i]];
402 if (C.do_h_vector || !using_GMP<Integer>())
403 gen_degrees_long[i] = C.gen_degrees_long[key[i]];
404 }
405
406 nr_level0_gens = 0;
407 level0_gen_degrees.clear();
408
409 if (C.inhomogeneous) {
410 for (i = 0; i < dim; i++) {
411 // gen_levels[i] = convertToLong(C.gen_levels[key[i]]);
412 gen_levels[i] = C.gen_levels[key[i]];
413 if (C.do_h_vector)
414 gen_levels_long[i] = convertToLong(C.gen_levels[key[i]]);
415 if (gen_levels[i] == 0) {
416 nr_level0_gens++;
417 if (C.do_h_vector)
418 level0_gen_degrees.push_back(gen_degrees_long[i]);
419 }
420 }
421 }
422
423 if (do_only_multiplicity) {
424 if (volume == 0) { // this means: not known in advance
425 volume = Generators.vol_submatrix(C.Generators, key);
426 #pragma omp atomic
427 TotDet++;
428 }
429 addMult(volume, Coll);
430 return volume;
431 } // done if only mult is asked for
432
433 for (i = 0; i < dim; ++i)
434 Generators[i] = C.Generators[key[i]];
435
436 bool unimodular = false;
437 bool GDiag_computed = false;
438 bool potentially_unimodular = (s.height == 1);
439
440 if (potentially_unimodular && C.isComputed(ConeProperty::Grading)) {
441 Integer g = 0;
442 for (i = 0; i < dim; ++i) {
443 g = libnormaliz::gcd(g, gen_degrees[i]);
444 if (g == 1)
445 break;
446 }
447 potentially_unimodular = (g == 1);
448 }
449
450 if (potentially_unimodular) { // very likely unimodular, Indicator computed first, uses transpose of Gen
451 RS_pointers.clear();
452 RS_pointers.push_back(&(C.Order_Vector));
453 LinSys.solve_system_submatrix_trans(Generators, id_key, RS_pointers, volume, 0,
454 1); // 1: replace components of solution by sign
455 for (i = 0; i < dim; i++)
456 Indicator[i] = LinSys[i][dim]; // extract solution
457
458 if (volume == 1) {
459 unimodular = true;
460 /* #pragma omp atomic
461 Unimod++; */
462 for (i = 0; i < dim; i++)
463 GDiag[i] = 1;
464 GDiag_computed = true;
465 }
466 /* else
467 #pragma omp atomic
468 Ht1NonUni++;*/
469 }
470
471 // we need the GDiag if not unimodular (to be computed from Gen)
472 // if potentially unimodular, we combine its computation with that of the i-th support forms for Ind[i]==0
473 // if unimodular and all Ind[i] !=0, then nothing is done here
474
475 vector<key_t> Ind0_key; // contains the indices i as above
476 Ind0_key.reserve(dim - 1);
477
478 if (potentially_unimodular)
479 for (i = 0; i < dim; i++)
480 if (Indicator[i] == 0)
481 Ind0_key.push_back(i);
482 if (!unimodular || Ind0_key.size() > 0) {
483 if (Ind0_key.size() > 0) {
484 RS_pointers = unit_matrix.submatrix_pointers(Ind0_key);
485 LinSys.solve_system_submatrix(Generators, id_key, RS_pointers, GDiag, volume, 0, RS_pointers.size());
486 // RS_pointers.size(): all columns of solution replaced by sign vevctors
487 for (size_t i = 0; i < dim; ++i)
488 for (size_t j = dim; j < dim + Ind0_key.size(); ++j)
489 InvGenSelCols[i][Ind0_key[j - dim]] = LinSys[i][j];
490
491 v_abs(GDiag);
492 GDiag_computed = true;
493 }
494 if (!GDiag_computed) {
495 RS_pointers.clear();
496 LinSys.solve_system_submatrix(Generators, id_key, RS_pointers, GDiag, volume, 0, 0);
497 v_abs(GDiag);
498 GDiag_computed = true;
499 }
500 }
501
502 // cout << "Vol " << volume << endl;
503
504 // take care of multiplicity unless do_only_multiplicity
505 // Can't be done earlier since volume is not always known earlier
506
507 addMult(volume, Coll);
508
509 if (unimodular && !C.do_h_vector && !C.do_Stanley_dec) { // in this case done
510 return volume;
511 }
512
513 // now we must compute the matrix InvGenSelRows (selected rows of InvGen)
514 // for those i for which Gdiag[i]>1 combined with computation
515 // of Indicator in case of potentially_unimodular==false (uses transpose of Gen)
516
517 vector<key_t> Last_key;
518 Last_key.reserve(dim);
519 if (!unimodular) {
520 for (i = 0; i < dim; ++i) {
521 if (GDiag[i] > 1)
522 Last_key.push_back(i);
523 }
524
525 RS_pointers = unit_matrix.submatrix_pointers(Last_key);
526
527 if (!potentially_unimodular) { // insert order vector if necessary
528 RS_pointers.push_back(&(C.Order_Vector));
529 }
530
531 // LinSys.solve_destructive(volume);
532 LinSys.solve_system_submatrix_trans(Generators, id_key, RS_pointers, volume, Last_key.size(),
533 RS_pointers.size() - Last_key.size());
534 // Last_key.dize(): these columns of solution reduced by volume
535 for (i = 0; i < Last_key.size(); i++) { // extract solutions as selected rows of InvGen
536 for (j = 0; j < dim; j++) {
537 InvGenSelRows[Last_key[i]][j] = LinSys[j][dim + i]; // %volume; //makes reduction mod volume easier
538 /* if(InvGenSelRows[Last_key[i]][j] <0) // Now in matrix.cpp
539 InvGenSelRows[Last_key[i]][j]+=volume;*/
540 }
541 }
542 if (!potentially_unimodular) { // extract Indicator
543 for (i = 0; i < dim; i++)
544 Indicator[i] = LinSys[i][dim + Last_key.size()];
545 }
546 }
547
548 // if not potentially unimodular we must still take care of the 0 ntries of the indicator
549
550 if (!potentially_unimodular) {
551 for (i = 0; i < dim; i++)
552 if (Indicator[i] == 0)
553 Ind0_key.push_back(i);
554 if (Ind0_key.size() > 0) {
555 RS_pointers = unit_matrix.submatrix_pointers(Ind0_key);
556 LinSys.solve_system_submatrix(Generators, id_key, RS_pointers, volume, 0, RS_pointers.size());
557 for (size_t i = 0; i < dim; ++i)
558 for (size_t j = dim; j < dim + Ind0_key.size(); ++j)
559 InvGenSelCols[i][Ind0_key[j - dim]] = LinSys[i][j];
560 }
561 }
562
563 // if (C.do_Hilbert_basis && C.descent_level > 0 && C.isComputed(ConeProperty::Grading)) {
564 // HB_bound = volume * C.God_Father->HB_bound;
565 // HB_bound_computed = true;
566 /* cout << "GF " << C.God_Father->HB_bound << " " << " VOL " << volume << " HB_bound " << HB_bound << endl;
567 cout << gen_degrees;
568 exit(0);*/
569 // }
570
571 /* if(Ind0_key.size()>0){
572 #pragma omp atomic
573 NonDecided++;
574 #pragma omp atomic
575 NonDecidedHyp+=Ind0_key.size();
576 }*/
577
578 return (volume);
579 }
580
581 //---------------------------------------------------------------------------
582
583 template <typename Integer>
find_excluded_facets()584 void SimplexEvaluator<Integer>::find_excluded_facets() {
585 size_t i, j;
586 Integer Test;
587 Deg0_offset = 0;
588 level_offset = 0; // level_offset is the level of the lement in par + its offset in the Stanley dec
589 for (i = 0; i < dim; i++)
590 Excluded[i] = false;
591 for (i = 0; i < dim; i++) { // excluded facets and degree shift for 0-vector
592 Test = Indicator[i];
593 if (Test < 0) {
594 Excluded[i] = true; // the facet opposite to vertex i is excluded
595 if (C_ptr->do_h_vector) {
596 Deg0_offset += gen_degrees_long[i];
597 if (C_ptr->inhomogeneous)
598 level_offset += gen_levels_long[i];
599 }
600 }
601 if (Test == 0) { // Order_Vector in facet, now lexicographic decision
602 for (j = 0; j < dim; j++) {
603 if (InvGenSelCols[j][i] < 0) { // COLUMNS of InvGen give supp hyps
604 Excluded[i] = true;
605 if (C_ptr->do_h_vector) {
606 Deg0_offset += gen_degrees_long[i];
607 if (C_ptr->inhomogeneous)
608 level_offset += gen_levels_long[i];
609 }
610 break;
611 }
612 if (InvGenSelCols[j][i] > 0) // facet included
613 break;
614 }
615 }
616 }
617 }
618
619 //---------------------------------------------------------------------------
620
621 template <typename Integer>
take_care_of_0vector(Collector<Integer> & Coll)622 void SimplexEvaluator<Integer>::take_care_of_0vector(Collector<Integer>& Coll) {
623 size_t i;
624 if (C_ptr->do_h_vector) {
625 if (C_ptr->inhomogeneous) {
626 if (level_offset <= 1)
627 update_inhom_hvector(level_offset, Deg0_offset, Coll); // here we count 0+offset
628 }
629 else {
630 Coll.hvector[Deg0_offset]++; // here we count 0+offset
631 }
632 }
633
634 // cout << "--- " << Coll.inhom_hvector;
635
636 if (C_ptr->do_excluded_faces)
637 prepare_inclusion_exclusion_simpl(Deg0_offset, Coll);
638
639 if (C_ptr->do_Stanley_dec) { // prepare space for Stanley dec
640 STANLEYDATA_int SimplStanley; // key + matrix of offsets
641 SimplStanley.key = key;
642 Matrix<Integer> offsets(convertToLong(volume), dim); // volume rows, dim columns
643 convert(SimplStanley.offsets, offsets);
644 #pragma omp critical(STANLEY)
645 {
646 C_ptr->StanleyDec.emplace_back(SimplStanley); // extend the Stanley dec by a new matrix
647 StanleyMat = &C_ptr->StanleyDec.back().offsets; // and use this matrix for storage
648 }
649 for (i = 0; i < dim; ++i) // the first vector is 0+offset
650 if (Excluded[i])
651 (*StanleyMat)[0][i] = convertToLong(volume);
652 }
653
654 StanIndex = 1; // counts the number of components in the Stanley dec. Vector at 0 already filled if necessary
655 }
656
657 //---------------------------------------------------------------------------
658
659 template <typename Integer>
transform_to_global(const vector<Integer> & element,vector<Integer> & help)660 void SimplexEvaluator<Integer>::transform_to_global(const vector<Integer>& element, vector<Integer>& help) {
661 bool success;
662 if (!GMP_transition) {
663 help = Generators.VxM_div(element, volume, success);
664 if (success)
665 return;
666
667 #pragma omp critical(MPZGEN)
668 {
669 if (!GMP_transition) {
670 mpz_Generators = Matrix<mpz_class>(dim, dim);
671 mat_to_mpz(Generators, mpz_Generators);
672 convert(mpz_volume, volume);
673 GMP_transition = true;
674 }
675 }
676 }
677
678 vector<mpz_class> mpz_element(dim);
679 convert(mpz_element, element);
680 vector<mpz_class> mpz_help = mpz_Generators.VxM_div(mpz_element, mpz_volume, success);
681 convert(help, mpz_help);
682 }
683
684 //---------------------------------------------------------------------------
685
686 // size_t NrSurvivors=0, NrCand=0;
687
688 //---------------------------------------------------------------------------
689
690 template <typename Integer>
evaluate_element(const vector<Integer> & element,Collector<Integer> & Coll)691 void SimplexEvaluator<Integer>::evaluate_element(const vector<Integer>& element, Collector<Integer>& Coll) {
692 INTERRUPT_COMPUTATION_BY_EXCEPTION
693
694 // now the vector in par has been produced and is in element
695 // DON'T FORGET: THE VECTOR PRODUCED IS THE "REAL" VECTOR*VOLUME !!
696
697 Integer norm;
698 Integer normG;
699 size_t i;
700
701 Full_Cone<Integer>& C = *C_ptr;
702
703 norm = 0; // norm is just the sum of coefficients, = volume*degree if homogenous
704 // it is used to sort the Hilbert basis candidates
705 normG = 0; // the degree according to the grading
706 for (i = 0; i < dim; i++) { // since generators have degree 1
707 norm += element[i];
708 if (C.do_h_vector || C.do_deg1_elements || HB_bound_computed) {
709 normG += element[i] * gen_degrees[i];
710 }
711 }
712
713 long level, level_offset = 0;
714 Integer level_Int = 0;
715
716 if (C.inhomogeneous) {
717 for (i = 0; i < dim; i++)
718 level_Int += element[i] * gen_levels[i];
719 level = convertToLong(level_Int / volume); // have to divide by volume; see above
720 // cout << level << " ++ " << volume << " -- " << element;
721
722 if (level > 1)
723 return; // ***************** nothing to do for this vector
724 // if we sahould decide to allow Stanley dec in the inhomogeneous case, this must be changed
725
726 // cout << "Habe ihn" << endl;
727
728 if (C.do_h_vector) {
729 level_offset = level;
730 for (i = 0; i < dim; i++)
731 if (element[i] == 0 && Excluded[i])
732 level_offset += gen_levels_long[i];
733 }
734 }
735
736 size_t Deg = 0;
737 if (C.do_h_vector) {
738 Deg = convertToLong(normG / volume);
739 for (i = 0; i < dim; i++) { // take care of excluded facets and increase degree when necessary
740 if (element[i] == 0 && Excluded[i]) {
741 Deg += gen_degrees_long[i];
742 }
743 }
744
745 // count point in the h-vector
746 if (C.inhomogeneous && level_offset <= 1)
747 update_inhom_hvector(level_offset, Deg, Coll);
748 else
749 Coll.hvector[Deg]++;
750
751 if (C.do_excluded_faces)
752 add_to_inex_faces(element, Deg, Coll);
753 }
754
755 if (C.do_Stanley_dec) {
756 convert((*StanleyMat)[StanIndex], element);
757 for (i = 0; i < dim; i++)
758 if (Excluded[i] && element[i] == 0)
759 (*StanleyMat)[StanIndex][i] += convertToLong(volume);
760 StanIndex++;
761 }
762
763 if (C.do_Hilbert_basis) {
764 if (HB_bound_computed) {
765 if (normG > HB_bound) {
766 return;
767 }
768 }
769 vector<Integer> candi = v_merge(element, norm);
770 if (C_ptr->do_module_gens_intcl || !is_reducible(candi, Hilbert_Basis)) {
771 Coll.Candidates.emplace_back(std::move(candi));
772 Coll.candidates_size++;
773 if (Coll.candidates_size >= 1000 && sequential_evaluation) {
774 local_reduction(Coll);
775 }
776 }
777 return;
778 }
779 if (C.do_deg1_elements && normG == volume && !isDuplicate(element)) {
780 vector<Integer> help(dim);
781 transform_to_global(element, help);
782 if (C.is_global_approximation && !C.subcone_contains(help)) {
783 return;
784 }
785 Coll.Deg1_Elements.emplace_back(std::move(help));
786 Coll.collected_elements_size++;
787 }
788 }
789
790 //---------------------------------------------------------------------------
791
792 template <typename Integer>
reduce_against_global(Collector<Integer> & Coll)793 void SimplexEvaluator<Integer>::reduce_against_global(Collector<Integer>& Coll) {
794 // inverse transformation and reduction against global reducers
795
796 Full_Cone<Integer>& C = *C_ptr;
797 bool inserted;
798 auto jj = Hilbert_Basis.begin();
799 for (; jj != Hilbert_Basis.end(); ++jj) {
800 jj->pop_back(); // remove the norm entry at the end
801
802 if (C.inhomogeneous && C.hilbert_basis_rec_cone_known) { // skip elements of the precomputed Hilbert basis
803 Integer level_Int = 0;
804 for (size_t i = 0; i < dim; i++)
805 level_Int += (*jj)[i] * gen_levels[i];
806 if (level_Int == 0)
807 continue;
808 }
809 if (!isDuplicate(*jj)) { // skip the element
810
811 // cout << "Vor " << *jj;
812 // transform to global coordinates
813 vector<Integer> help = *jj; // we need a copy
814 transform_to_global(help, *jj);
815 // v_scalar_division(*jj,volume);
816 // cout << "Nach " << *jj;
817
818 // reduce against global reducers in C.OldCandidates and insert into HB_Elements
819 if (C.is_simplicial) { // no global reduction necessary at this point
820 Coll.HB_Elements.Candidates.emplace_back(Candidate<Integer>(*jj, C));
821 inserted = true;
822 }
823 else
824 inserted = Coll.HB_Elements.reduce_by_and_insert(*jj, C, C.OldCandidates);
825 // cout << "iiiii " << inserted << " -- " << *jj << endl;
826
827 if(inserted && C.do_integrally_closed){ // we must safeduard against original generators
828 auto gen = C.Generator_Set.find(*jj); // that appear in the Hilbert basis of
829 if(gen != C.Generator_Set.end()) // this simplicial cone
830 inserted = false;
831 }
832
833 if (inserted) {
834 Coll.collected_elements_size++;
835 if (C.do_integrally_closed) {
836 #pragma omp critical(INTEGRALLY_CLOSED)
837 {
838 C.do_Hilbert_basis = false;
839 C.Witness = *jj;
840 C.is_Computed.set(ConeProperty::WitnessNotIntegrallyClosed);
841 } // critical
842 if (!C.do_triangulation) {
843 throw NotIntegrallyClosedException();
844 }
845 }
846
847 /*
848 if (C.God_Father->do_integrally_closed && C.is_simplicial) {
849 bool GF_inserted = Coll.HB_Elements.reduce_by_and_insert(*jj, *(C.God_Father), C.God_Father->OldCandidates);
850 if (GF_inserted) {
851 #pragma omp critical
852 {
853 C.do_Hilbert_basis = false;
854 C.God_Father->do_Hilbert_basis = false;
855 C.Witness = *jj;
856 C.is_Computed.set(ConeProperty::WitnessNotIntegrallyClosed);
857 }
858 if (!C.do_triangulation) {
859 throw NotIntegrallyClosedException();
860 }
861 }
862 }
863 */
864 }
865 }
866 }
867 // Coll.HB_Elements.search();
868 }
869
870 //---------------------------------------------------------------------------
871
872 template <typename Integer>
add_hvect_to_HS(Collector<Integer> & Coll)873 void SimplexEvaluator<Integer>::add_hvect_to_HS(Collector<Integer>& Coll) {
874 Full_Cone<Integer>& C = *C_ptr;
875
876 if (C.do_h_vector) {
877 if (C.inhomogeneous) {
878 Coll.Hilbert_Series.add(Coll.inhom_hvector, level0_gen_degrees);
879 for (size_t i = 0; i < Coll.inhom_hvector.size(); i++)
880 Coll.inhom_hvector[i] = 0;
881 // cout << "WAU " << endl;
882 }
883 else {
884 Coll.Hilbert_Series.add(Coll.hvector, gen_degrees_long);
885 for (size_t i = 0; i < Coll.hvector.size(); i++)
886 Coll.hvector[i] = 0;
887 if (C.do_excluded_faces)
888 for (size_t i = 0; i < nrInExSimplData; ++i) {
889 Coll.Hilbert_Series.add(Coll.InEx_hvector[i], InExSimplData[i].gen_degrees);
890 for (size_t j = 0; j < Coll.InEx_hvector[i].size(); ++j)
891 Coll.InEx_hvector[i][j] = 0;
892 }
893 }
894 }
895
896 // cout << Coll.Hilbert_Series << endl;
897 }
898
899 //---------------------------------------------------------------------------
900
901 template <typename Integer>
conclude_evaluation(Collector<Integer> & Coll)902 void SimplexEvaluator<Integer>::conclude_evaluation(Collector<Integer>& Coll) {
903 Full_Cone<Integer>& C = *C_ptr;
904
905 add_hvect_to_HS(Coll);
906
907 if (volume == 1 || !C.do_Hilbert_basis || !sequential_evaluation)
908 return; // no further action in this case
909
910 // cout << "Starting local reduction" << endl;
911
912 local_reduction(Coll);
913
914 // cout << "local HB " << Hilbert_Basis.size() << endl;
915
916 reduce_against_global(Coll);
917
918 // cout << "local reduction finished " << Coll.collected_elements_size << endl;
919
920 Hilbert_Basis.clear(); // this is not a local variable !!
921 }
922
923 //---------------------------------------------------------------------------
924
925 long SimplexParallelEvaluationBound = 100000000; // simplices larger than this bound/10
926 // are evaluated by parallel threads
927 // simplices larger than this bound || (this bound/10 && Hilbert basis)
928 // are tried for subdivision
929
930 //---------------------------------------------------------------------------
931
932 /* evaluates a simplex in regard to all data in a single thread*/
933 template <typename Integer>
evaluate(SHORTSIMPLEX<Integer> & s)934 bool SimplexEvaluator<Integer>::evaluate(SHORTSIMPLEX<Integer>& s) {
935 start_evaluation(s, C_ptr->Results[tn]);
936 s.vol = volume;
937 if (C_ptr->do_only_multiplicity)
938 return true;
939 find_excluded_facets();
940 if (C_ptr->do_cone_dec)
941 s.Excluded = Excluded;
942 // large simplicies to be postponed for parallel evaluation
943 if (volume > SimplexParallelEvaluationBound / 10
944 // || (volume > SimplexParallelEvaluationBound/10 && C_ptr->do_Hilbert_basis) )
945 && !C_ptr->do_Stanley_dec) { //&& omp_get_max_threads()>1)
946 return false;
947 }
948 if (C_ptr->stop_after_cone_dec)
949 return true;
950 take_care_of_0vector(C_ptr->Results[tn]);
951 if (volume != 1)
952 evaluate_block(1, convertToLong(volume) - 1, C_ptr->Results[tn]);
953 conclude_evaluation(C_ptr->Results[tn]);
954
955 return true;
956 }
957
958 //---------------------------------------------------------------------------
959
960 const size_t ParallelBlockLength = 10000; // the length of the block of elements to be processed by a thread
961 // const size_t MaxNrBlocks=20000; // maximum number of blocks
962 const size_t LocalReductionBound = 10000; // number of candidates in a thread starting local reduction
963 const size_t SuperBlockLength = 1000000; // number of blocks in a super block
964
965 //---------------------------------------------------------------------------
966 // The following routiner organizes the evaluation of a single large simplex in parallel trhreads.
967 // This evaluation can be split into "superblocks" whose blocks are then run in parallel.
968 // The reason or the existence of superblocks is the joint local reduction of the common results of
969 // the individual blocks. Each block gets its parallel thread, and is done sequentially by this thread.
970 // When the blockas in a superblock have been finished, the resulrs are transferred to the collector
971 // of thread 0, and a local reduction is applied to it.
972 // The joint local reduction is also done when a single trgrad has collected LocalReductionBound many
973 // Hilbert basis elements.
974 // Superblocks were introduced to give a better progress report of the current computation.
975 template <typename Integer>
evaluation_loop_parallel()976 void SimplexEvaluator<Integer>::evaluation_loop_parallel() {
977 size_t block_length = ParallelBlockLength;
978 size_t nr_elements = convertToLong(volume) - 1; // 0-vector already taken care of
979 size_t nr_blocks = nr_elements / ParallelBlockLength;
980 if (nr_elements % ParallelBlockLength != 0)
981 ++nr_blocks;
982
983 size_t nr_superblocks = nr_blocks / SuperBlockLength;
984 if (nr_blocks % SuperBlockLength != 0)
985 nr_superblocks++;
986
987 for (size_t sbi = 0; sbi < nr_superblocks; sbi++) {
988 if (C_ptr->verbose && nr_superblocks > 1) {
989 if (sbi > 0)
990 verboseOutput() << endl;
991 verboseOutput() << "Superblock " << sbi + 1 << " ";
992 }
993
994 size_t actual_nr_blocks;
995
996 if (sbi == nr_superblocks - 1 && nr_blocks % SuperBlockLength != 0) // the last round of smaller length
997 actual_nr_blocks = nr_blocks % SuperBlockLength;
998 else
999 actual_nr_blocks = SuperBlockLength;
1000
1001 size_t progess_report = actual_nr_blocks / 50;
1002 if (progess_report == 0)
1003 progess_report = 1;
1004
1005 bool skip_remaining;
1006 std::exception_ptr tmp_exception;
1007
1008 deque<bool> done(actual_nr_blocks, false);
1009
1010 do {
1011 skip_remaining = false;
1012 sequential_evaluation = false;
1013
1014 #pragma omp parallel
1015 {
1016 int tn = omp_get_thread_num(); // chooses the associated collector Results[tn]
1017
1018 #pragma omp for schedule(dynamic)
1019 for (size_t i = 0; i < actual_nr_blocks; ++i) {
1020 if (skip_remaining || done[i])
1021 continue;
1022 try {
1023 if (C_ptr->verbose) {
1024 if (i > 0 && i % progess_report == 0)
1025 verboseOutput() << "." << flush;
1026 }
1027 done[i] = true;
1028 long block_start = (sbi * SuperBlockLength + i) * block_length + 1; // we start at 1
1029 long block_end = block_start + block_length - 1;
1030 if (block_end > (long)nr_elements)
1031 block_end = nr_elements;
1032 evaluate_block(block_start, block_end, C_ptr->Results[tn]);
1033 if (C_ptr->Results[tn].candidates_size >= LocalReductionBound) // >= (not > !! ) if
1034 skip_remaining = true; // LocalReductionBound==ParallelBlockLength
1035 } catch (const std::exception&) {
1036 tmp_exception = std::current_exception();
1037 skip_remaining = true;
1038 #pragma omp flush(skip_remaining)
1039 }
1040 } // for
1041
1042 } // parallel
1043
1044 sequential_evaluation = true;
1045
1046 if (!(tmp_exception == 0))
1047 std::rethrow_exception(tmp_exception);
1048
1049 if (skip_remaining) {
1050 if (C_ptr->verbose) {
1051 verboseOutput() << "r" << flush;
1052 }
1053 collect_vectors();
1054 local_reduction(C_ptr->Results[0]);
1055 }
1056
1057 } while (skip_remaining);
1058
1059 } // superblock loop
1060 }
1061
1062 //---------------------------------------------------------------------------
1063 // runs the evaluation over all vectors in the basic parallelotope that are
1064 // produced from block_start to block_end.
1065 template <typename Integer>
evaluate_block(long block_start,long block_end,Collector<Integer> & Coll)1066 void SimplexEvaluator<Integer>::evaluate_block(long block_start, long block_end, Collector<Integer>& Coll) {
1067 size_t last;
1068 vector<Integer> point(dim, 0); // represents the lattice element whose residue class is to be processed
1069
1070 Matrix<Integer>& elements = Coll.elements;
1071 elements.set_zero();
1072
1073 size_t one_back = block_start - 1;
1074 long counter = one_back;
1075
1076 if (one_back > 0) { // define the last point processed before if it isn't 0
1077 for (size_t i = 1; i <= dim; ++i) {
1078 point[dim - i] = static_cast<unsigned long>(one_back) % GDiag[dim - i];
1079 one_back /= convertToLong(GDiag[dim - i]);
1080 }
1081
1082 for (size_t i = 0; i < dim; ++i) { // put elements into the state at the end of the previous block
1083 if (point[i] != 0) {
1084 elements[i] = v_add(elements[i], v_scalar_mult_mod(InvGenSelRows[i], point[i], volume));
1085 v_reduction_modulo(elements[i], volume);
1086 for (size_t j = i + 1; j < dim; ++j)
1087 elements[j] = elements[i];
1088 }
1089 }
1090 }
1091
1092 // cout << "VOl " << volume << " " << counter << " " << block_end << endl;
1093 // cout << point;
1094 // cout << GDiag;
1095
1096 // now we create the elements in par
1097 while (true) {
1098 last = dim;
1099 for (int k = dim - 1; k >= 0; k--) {
1100 if (point[k] < GDiag[k] - 1) {
1101 last = k;
1102 break;
1103 }
1104 }
1105 if (counter >= block_end) {
1106 break;
1107 }
1108
1109 counter++;
1110
1111 // cout << "COUNTER " << counter << " LAST " << last << endl;
1112
1113 point[last]++;
1114 v_add_to_mod(elements[last], InvGenSelRows[last], volume);
1115
1116 for (size_t i = last + 1; i < dim; i++) {
1117 point[i] = 0;
1118 elements[i] = elements[last];
1119 }
1120
1121 // cout << "COUNTER " << counter << " LAST " << elements[last];
1122
1123 evaluate_element(elements[last], Coll);
1124 }
1125 }
1126
1127 template <>
evaluate_block(long block_start,long block_end,Collector<renf_elem_class> & Coll)1128 void SimplexEvaluator<renf_elem_class>::evaluate_block(long block_start, long block_end, Collector<renf_elem_class>& Coll) {
1129
1130 assert(false);
1131
1132 }
1133
1134 //---------------------------------------------------------------------------
1135
1136 /* transfer the vector lists in the collectors to C_ptr->Results[0] */
1137 template <typename Integer>
collect_vectors()1138 void SimplexEvaluator<Integer>::collect_vectors() {
1139 if (C_ptr->do_Hilbert_basis) {
1140 for (size_t i = 1; i < C_ptr->Results.size(); ++i) {
1141 C_ptr->Results[0].Candidates.splice(C_ptr->Results[0].Candidates.end(), C_ptr->Results[i].Candidates);
1142 C_ptr->Results[0].candidates_size += C_ptr->Results[i].candidates_size;
1143 C_ptr->Results[i].candidates_size = 0;
1144 }
1145 }
1146 }
1147
1148 //---------------------------------------------------------------------------
1149
1150 /* evaluates a simplex in parallel threads */
1151 template <typename Integer>
Simplex_parallel_evaluation()1152 void SimplexEvaluator<Integer>::Simplex_parallel_evaluation() {
1153 /* Generators.pretty_print(cout);
1154 cout << "==========================" << endl; */
1155
1156 if (C_ptr->verbose) {
1157 verboseOutput() << "simplex volume " << volume << endl;
1158 }
1159
1160 if (C_ptr->use_bottom_points &&
1161 (volume >= SimplexParallelEvaluationBound || (volume > SimplexParallelEvaluationBound / 10 && C_ptr->do_Hilbert_basis)) &&
1162 (!C_ptr->deg1_triangulation || !C_ptr->isComputed(ConeProperty::Grading))) { // try subdivision
1163
1164 Full_Cone<Integer>& C = *C_ptr;
1165
1166 assert(C.omp_start_level == omp_get_level()); // make sure that we are on the lowest parallelization level
1167
1168 if (C_ptr->verbose) {
1169 verboseOutput() << "**************************************************" << endl;
1170 verboseOutput() << "Try to decompose the simplex into smaller simplices." << endl;
1171 }
1172
1173 for (size_t i = 0; i < dim; ++i)
1174 Generators[i] = C.Generators[key[i]];
1175
1176 list<vector<Integer> > new_points;
1177 time_t start, end;
1178 time(&start);
1179 void (*prev_handler)(int);
1180 prev_handler = signal(SIGINT, SIG_IGN); // we don't want to set a new handler here
1181 signal(SIGINT, prev_handler);
1182
1183 bottom_points(new_points, Generators, volume);
1184
1185 signal(SIGINT, prev_handler);
1186
1187 time(&end);
1188 double dif = difftime(end, start);
1189
1190 if (C_ptr->verbose) {
1191 verboseOutput() << "Bottom points took " << dif << " sec " << endl;
1192 }
1193
1194 // cout << new_points.size() << " new points " << endl << new_points << endl;
1195 if (!new_points.empty()) {
1196 C.triangulation_is_nested = true;
1197 // add new_points to the Top_Cone generators
1198 int nr_new_points = new_points.size();
1199 int nr_old_gen = C.nr_gen;
1200 Matrix<Integer> new_points_mat(new_points);
1201 C.add_generators(new_points_mat);
1202 // remove this simplex from det_sum and multiplicity
1203 addMult(-volume, C.Results[0]);
1204 // delete this large simplex
1205 C.totalNrSimplices--;
1206 if (C.keep_triangulation) {
1207 for (auto it = C.Triangulation.begin(); it != C.Triangulation.end(); ++it) {
1208 if (it->key == key) {
1209 C.Triangulation.erase(it);
1210 break;
1211 }
1212 }
1213 }
1214
1215 // create generators for bottom decomposition
1216 // we start with the extreme rays of the recession cone
1217 Matrix<Integer> BotGens = Generators;
1218 BotGens.append_column(vector<Integer>(dim, 0));
1219 // now the polyhedron
1220 vector<key_t> subcone_key(C.dim + nr_new_points);
1221 for (size_t i = 0; i < C.dim; ++i) {
1222 subcone_key[i] = key[i];
1223 }
1224 for (int i = 0; i < nr_new_points; ++i) {
1225 subcone_key[C.dim + i] = nr_old_gen + i;
1226 }
1227 Matrix<Integer> polytope_gens(C.Generators.submatrix(subcone_key));
1228 polytope_gens.append_column(vector<Integer>(polytope_gens.nr_of_rows(), 1));
1229 BotGens.append(polytope_gens);
1230
1231 // compute bottom decomposition
1232 Full_Cone<Integer> bottom_polytope(BotGens);
1233 bottom_polytope.keep_order = true;
1234 // bottom_polytope.verbose=true;
1235 if (C_ptr->verbose) {
1236 verboseOutput() << "Computing bottom decomposition ... " << flush;
1237 }
1238 time(&start);
1239 bottom_polytope.dualize_cone(false);
1240 time(&end);
1241 dif = difftime(end, start);
1242 if (C_ptr->verbose) {
1243 verboseOutput() << "done." << endl;
1244 verboseOutput() << "Bottom decomposition took " << dif << " sec" << endl;
1245 }
1246 assert(bottom_polytope.isComputed(ConeProperty::SupportHyperplanes));
1247
1248 // extract bottom decomposition
1249 for (size_t i = 0; i < bottom_polytope.Support_Hyperplanes.nr_of_rows(); ++i) {
1250 INTERRUPT_COMPUTATION_BY_EXCEPTION
1251
1252 if (bottom_polytope.Support_Hyperplanes[i][dim] >= 0) // not a bottom facet
1253 continue;
1254 vector<key_t> bottom_key;
1255 for (size_t j = 0; j < polytope_gens.nr_of_rows(); ++j) {
1256 if (v_scalar_product(polytope_gens[j], bottom_polytope.Support_Hyperplanes[i]) == 0)
1257 bottom_key.push_back(subcone_key[j]);
1258 }
1259 C.Pyramids[0].emplace_back(std::move(bottom_key));
1260 C.nrPyramids[0]++;
1261 }
1262
1263 if (C_ptr->verbose) {
1264 verboseOutput() << "**************************************************" << endl;
1265 }
1266
1267 return;
1268 }
1269 } // end subdivision
1270
1271 take_care_of_0vector(C_ptr->Results[0]);
1272
1273 evaluation_loop_parallel();
1274
1275 collect_vectors(); // --> Results[0]
1276 for (size_t i = 1; i < C_ptr->Results.size(); ++i) // takes care of h-vectors
1277 add_hvect_to_HS(C_ptr->Results[i]);
1278 conclude_evaluation(C_ptr->Results[0]); // h-vector in Results[0] and collected elements
1279
1280 if (C_ptr->verbose) {
1281 verboseOutput() << endl;
1282 }
1283 }
1284
1285 //---------------------------------------------------------------------------
1286
1287 template <typename Integer>
isDuplicate(const vector<Integer> & cand) const1288 bool SimplexEvaluator<Integer>::isDuplicate(const vector<Integer>& cand) const {
1289 for (size_t i = 0; i < dim; i++)
1290 if (cand[i] == 0 && Excluded[i])
1291 return true;
1292 return false;
1293 }
1294
1295 //---------------------------------------------------------------------------
1296
1297 template <typename Integer>
update_mult_inhom(Integer & multiplicity)1298 void SimplexEvaluator<Integer>::update_mult_inhom(Integer& multiplicity) {
1299 if (!C_ptr->isComputed(ConeProperty::Grading) || !C_ptr->do_triangulation)
1300 return;
1301 if (C_ptr->level0_dim == dim - 1) { // the case of codimension 1
1302 size_t i;
1303 for (i = 0; i < dim; ++i)
1304 if (gen_levels[i] > 0) {
1305 break;
1306 }
1307 assert(i < dim);
1308 multiplicity *= gen_degrees[i]; // to correct division in addMult_inner
1309 multiplicity /= gen_levels[i];
1310 }
1311 else {
1312 size_t i, j = 0;
1313 Integer corr_fact = 1;
1314 for (i = 0; i < dim; ++i)
1315 if (gen_levels[i] > 0) {
1316 ProjGen[j] = C_ptr->ProjToLevel0Quot.MxV(C_ptr->Generators[key[i]]); // Generators of evaluator may be destroyed
1317 corr_fact *= gen_degrees[i];
1318 j++;
1319 }
1320 multiplicity *= corr_fact;
1321 multiplicity /= ProjGen.vol(); // .vol_destructive();
1322 // cout << "After corr " << multiplicity << endl;
1323 }
1324 }
1325
1326 //---------------------------------------------------------------------------
1327
1328 template <typename Integer>
addMult(Integer multiplicity,Collector<Integer> & Coll)1329 void SimplexEvaluator<Integer>::addMult(Integer multiplicity, Collector<Integer>& Coll) {
1330 assert(multiplicity != 0);
1331 Coll.det_sum += multiplicity;
1332 if (!C_ptr->isComputed(ConeProperty::Grading) || !C_ptr->do_triangulation ||
1333 (C_ptr->inhomogeneous && nr_level0_gens != C_ptr->level0_dim))
1334 return;
1335
1336 if (C_ptr->inhomogeneous) {
1337 update_mult_inhom(multiplicity);
1338 }
1339
1340 if (C_ptr->deg1_triangulation) {
1341 Coll.mult_sum += convertTo<mpz_class>(multiplicity);
1342 }
1343 else {
1344 if (using_GMP<Integer>()) {
1345 mpz_class deg_prod = convertTo<mpz_class>(gen_degrees[0]);
1346 for (size_t i = 1; i < dim; i++) {
1347 deg_prod *= convertTo<mpz_class>(gen_degrees[i]);
1348 }
1349 mpq_class mult = convertTo<mpz_class>(multiplicity);
1350 mult /= deg_prod;
1351 Coll.mult_sum += mult;
1352 }
1353 else {
1354 mpz_class deg_prod = gen_degrees_long[0];
1355 for (size_t i = 1; i < dim; i++) {
1356 deg_prod *= gen_degrees_long[i];
1357 }
1358 mpq_class mult = convertTo<mpz_class>(multiplicity);
1359 mult /= deg_prod;
1360 Coll.mult_sum += mult;
1361 }
1362 }
1363 }
1364 //---------------------------------------------------------------------------
1365
1366 template <typename Integer>
local_reduction(Collector<Integer> & Coll)1367 void SimplexEvaluator<Integer>::local_reduction(Collector<Integer>& Coll) {
1368 // reduce new against old elements
1369
1370 assert(sequential_evaluation);
1371 Coll.Candidates.sort(compare_last<Integer>);
1372
1373 if (C_ptr->do_module_gens_intcl) { // in this case there is no local reduction
1374 Hilbert_Basis.splice(Hilbert_Basis.begin(), Coll.Candidates); // but direct reduction against global old candidates
1375 reduce_against_global(Coll);
1376 Hilbert_Basis.clear();
1377 Coll.candidates_size = 0;
1378 return;
1379 }
1380
1381 // interreduce
1382 reduce(Coll.Candidates, Coll.Candidates, Coll.candidates_size);
1383
1384 // reduce old elements by new ones
1385 count_and_reduce(Hilbert_Basis, Coll.Candidates);
1386 Hilbert_Basis.merge(Coll.Candidates, compare_last<Integer>);
1387 Coll.candidates_size = 0;
1388 }
1389
1390 template <typename Integer>
count_and_reduce(list<vector<Integer>> & Candi,list<vector<Integer>> & Reducers)1391 void SimplexEvaluator<Integer>::count_and_reduce(list<vector<Integer> >& Candi, list<vector<Integer> >& Reducers) {
1392 size_t dummy = Candi.size();
1393 reduce(Candi, Reducers, dummy);
1394 }
1395
1396 template <typename Integer>
reduce(list<vector<Integer>> & Candi,list<vector<Integer>> & Reducers,size_t & Candi_size)1397 void SimplexEvaluator<Integer>::reduce(list<vector<Integer> >& Candi, list<vector<Integer> >& Reducers, size_t& Candi_size) {
1398 // This parallel region cannot throw a NormalizException
1399 #pragma omp parallel
1400 {
1401 auto cand = Candi.begin();
1402 size_t jjpos = 0;
1403
1404 #pragma omp for schedule(dynamic)
1405 for (size_t j = 0; j < Candi_size; ++j) { // remove negative subfacets shared
1406 for (; j > jjpos; ++jjpos, ++cand)
1407 ; // by non-simpl neg or neutral facets
1408 for (; j < jjpos; --jjpos, --cand)
1409 ;
1410
1411 if (is_reducible(*cand, Reducers))
1412 (*cand)[dim] = 0; // mark the candidate
1413 }
1414
1415 } // parallel
1416
1417 auto cand = Candi.begin(); // remove reducibles
1418 while (cand != Candi.end()) {
1419 if ((*cand)[dim] == 0) {
1420 cand = Candi.erase(cand);
1421 --Candi_size;
1422 }
1423 else
1424 ++cand;
1425 }
1426 }
1427
1428 template <typename Integer>
is_reducible(const vector<Integer> & new_element,list<vector<Integer>> & Reducers)1429 bool SimplexEvaluator<Integer>::is_reducible(const vector<Integer>& new_element, list<vector<Integer> >& Reducers) {
1430 // the norm is at position dim
1431
1432 size_t i, c = 0;
1433 for (const auto& red : Reducers) {
1434 if (new_element[dim] < 2 * red[dim]) {
1435 break; // new_element is not reducible;
1436 }
1437 else {
1438 if (red[c] <= new_element[c]) {
1439 for (i = 0; i < dim; i++) {
1440 if (red[i] > new_element[i]) {
1441 c = i;
1442 break;
1443 }
1444 }
1445 if (i == dim) {
1446 return true;
1447 }
1448 // new_element is not in the Hilbert Basis
1449 }
1450 }
1451 }
1452 return false;
1453 }
1454
1455 //---------------------------------------------------------------------------
1456
1457 template <typename Integer>
print_all()1458 void SimplexEvaluator<Integer>::print_all() {
1459 // C_ptr(&fc),
1460 // dim(fc.dim),
1461 // key(dim)
1462 cout << "print all matricies" << endl;
1463 cout << "Generators" << endl;
1464 Generators.pretty_print(cout);
1465 cout << "GenCopy" << endl;
1466 GenCopy.pretty_print(cout);
1467 cout << "InvGenSelRows" << endl;
1468 InvGenSelRows.pretty_print(cout);
1469 cout << "InvGenSelCols" << endl;
1470 InvGenSelCols.pretty_print(cout);
1471 cout << "Sol" << endl;
1472 Sol.pretty_print(cout);
1473 // ProjGen(dim-fc.level0_dim,dim-fc.level0_dim),
1474 cout << "RS" << endl;
1475 RS.pretty_print(cout);
1476 cout << "StanleyMat" << endl;
1477 // St.pretty_print(cout);
1478 // GDiag(dim),
1479 // TDiag(dim),
1480 // Excluded(dim),
1481 // Indicator(dim),
1482 // gen_degrees(dim),
1483 // gen_levels(dim),
1484 // RS(dim,1),
1485 // InExSimplData(C_ptr->InExCollect.size())
1486 }
1487
1488 //---------------------------------------------------------------------------
1489
1490 template <typename Integer>
get_key()1491 vector<key_t> SimplexEvaluator<Integer>::get_key() {
1492 return key;
1493 }
1494
1495 template <typename Integer>
get_volume()1496 Integer SimplexEvaluator<Integer>::get_volume() {
1497 return volume;
1498 }
1499
1500 // Collector
1501
1502 template <typename Integer>
Collector(Full_Cone<Integer> & fc)1503 Collector<Integer>::Collector(Full_Cone<Integer>& fc)
1504 : C_ptr(&fc),
1505 dim(fc.dim),
1506 det_sum(0),
1507 mult_sum(0),
1508 candidates_size(0),
1509 collected_elements_size(0),
1510 InEx_hvector(C_ptr->InExCollect.size()),
1511 elements(dim, dim) {
1512 size_t hv_max = 0;
1513 if (C_ptr->do_h_vector) {
1514 // we need the generators to be sorted by degree
1515 long max_degree = convertToLong(C_ptr->gen_degrees[C_ptr->nr_gen - 1]);
1516 hv_max = max_degree * C_ptr->dim;
1517 if (hv_max > 1000000) {
1518 throw BadInputException("Generator degrees are too huge, h-vector would contain more than 10^6 entires.");
1519 }
1520
1521 hvector.resize(hv_max, 0);
1522 inhom_hvector.resize(hv_max, 0);
1523 }
1524 for (size_t i = 0; i < InEx_hvector.size(); ++i)
1525 InEx_hvector[i].resize(hv_max, 0);
1526 Hilbert_Series.setVerbose(fc.verbose);
1527 }
1528
1529 template <>
Collector(Full_Cone<renf_elem_class> & fc)1530 Collector<renf_elem_class>::Collector(Full_Cone<renf_elem_class>& fc)
1531 : C_ptr(&fc),
1532 dim(fc.dim),
1533 det_sum(0),
1534 mult_sum(0),
1535 candidates_size(0),
1536 collected_elements_size(0),
1537 InEx_hvector(C_ptr->InExCollect.size()),
1538 elements(dim, dim) {
1539 }
1540 template <typename Integer>
getDetSum() const1541 Integer Collector<Integer>::getDetSum() const {
1542 return det_sum;
1543 }
1544
1545 template <typename Integer>
getMultiplicitySum() const1546 mpq_class Collector<Integer>::getMultiplicitySum() const {
1547 return mult_sum;
1548 }
1549
1550 template <typename Integer>
getHilbertSeriesSum() const1551 const HilbertSeries& Collector<Integer>::getHilbertSeriesSum() const {
1552 return Hilbert_Series;
1553 }
1554
1555 template <typename Integer>
transfer_candidates()1556 void Collector<Integer>::transfer_candidates() {
1557 if (collected_elements_size == 0)
1558 return;
1559 if (C_ptr->do_Hilbert_basis) {
1560 #pragma omp critical(CANDIDATES)
1561 C_ptr->NewCandidates.splice(HB_Elements);
1562 #pragma omp atomic
1563 C_ptr->CandidatesSize += collected_elements_size;
1564 }
1565 if (C_ptr->do_deg1_elements) {
1566 #pragma omp critical(CANDIDATES)
1567 C_ptr->Deg1_Elements.splice(C_ptr->Deg1_Elements.begin(), Deg1_Elements);
1568 #pragma omp atomic
1569 C_ptr->CandidatesSize += collected_elements_size;
1570 }
1571
1572 collected_elements_size = 0;
1573 }
1574
1575 template <typename Integer>
get_collected_elements_size()1576 size_t Collector<Integer>::get_collected_elements_size() {
1577 return collected_elements_size;
1578 }
1579
1580 #ifndef NMZ_MIC_OFFLOAD // offload with long is not supported
1581 template class SimplexEvaluator<long>;
1582 #endif
1583 template class SimplexEvaluator<long long>;
1584 template class SimplexEvaluator<mpz_class>;
1585
1586 #ifdef ENFNORMALIZ
1587 template class SimplexEvaluator<renf_elem_class>;
1588 #endif
1589
1590 #ifndef NMZ_MIC_OFFLOAD // offload with long is not supported
1591 template class Collector<long>;
1592 #endif
1593 template class Collector<long long>;
1594 template class Collector<mpz_class>;
1595
1596 #ifdef ENFNORMALIZ
1597 template class Collector<renf_elem_class>;
1598 #endif
1599
1600 } // namespace libnormaliz
1601