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