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 <cstdlib>
27 #include <vector>
28 #include <map>
29 #include <set>
30 #include <iostream>
31 #include <string>
32 #include <algorithm>
33 
34 #include "libnormaliz/cone_dual_mode.h"
35 #include "libnormaliz/vector_operations.h"
36 #include "libnormaliz/list_and_map_operations.h"
37 #include "libnormaliz/full_cone.h"
38 // #include "libnormaliz/cone_helper.h"
39 #include "libnormaliz/my_omp.h"
40 
41 //---------------------------------------------------------------------------
42 
43 namespace libnormaliz {
44 using namespace std;
45 
46 //---------------------------------------------------------------------------
47 // private
48 //---------------------------------------------------------------------------
49 
50 template <typename Integer>
splice_them_sort(CandidateList<Integer> & Total,vector<CandidateList<Integer>> & Parts)51 void Cone_Dual_Mode<Integer>::splice_them_sort(CandidateList<Integer>& Total, vector<CandidateList<Integer> >& Parts) {
52     CandidateList<Integer> New;
53     New.verbose = verbose;
54     New.dual = true;
55     for (int i = 0; i < omp_get_max_threads(); i++)
56         New.Candidates.splice(New.Candidates.end(), Parts[i].Candidates);
57     New.sort_by_val();
58     New.unique_vectors();
59     Total.merge_by_val(New);
60 }
61 
62 //---------------------------------------------------------------------------
63 
64 template <typename Integer>
select_HB(CandidateList<Integer> & Cand,size_t guaranteed_HB_deg,CandidateList<Integer> & Irred,bool all_irreducible)65 void Cone_Dual_Mode<Integer>::select_HB(CandidateList<Integer>& Cand,
66                                         size_t guaranteed_HB_deg,
67                                         CandidateList<Integer>& Irred,
68                                         bool all_irreducible) {
69     if (all_irreducible) {
70         Irred.merge_by_val(Cand);
71         return;
72     }
73 
74     for (auto h = Cand.Candidates.begin(); h != Cand.Candidates.end();) {
75         if (h->old_tot_deg <= guaranteed_HB_deg) {
76             Irred.Candidates.splice(Irred.Candidates.end(), Cand.Candidates, h++);
77         }
78         else {
79             ++h;
80         }
81     }
82     Irred.auto_reduce_sorted();  // necessary since the guaranteed HB degree only determines
83                                  // in which degrees we can already decide whether an element belongs to the HB
84 }
85 
86 //---------------------------------------------------------------------------
87 
88 // public
89 //---------------------------------------------------------------------------
90 
91 template <typename Integer>
Cone_Dual_Mode(Matrix<Integer> & M,const vector<Integer> & Truncation,bool keep_order)92 Cone_Dual_Mode<Integer>::Cone_Dual_Mode(Matrix<Integer>& M, const vector<Integer>& Truncation, bool keep_order) {
93     dim = M.nr_of_columns();
94     M.remove_duplicate_and_zero_rows();
95     // now we sort by L_1-norm and then lex
96     if (!keep_order) {
97         Matrix<Integer> Weights(0, dim);
98         vector<bool> absolute;
99         Weights.append(vector<Integer>(dim, 1));
100         absolute.push_back(true);
101         vector<key_t> perm = M.perm_by_weights(Weights, absolute);
102         M.order_rows_by_perm(perm);
103     }
104 
105     SupportHyperplanes = Matrix<Integer>(0, dim);
106     BasisMaxSubspace = Matrix<Integer>(dim);  // dim x dim identity matrix
107     if (Truncation.size() != 0) {
108         vector<Integer> help = Truncation;
109         v_make_prime(help);                     // truncation need not be coprime
110         M.remove_row(help);                     // remove truncation if it should be a support hyperplane
111         SupportHyperplanes.append(Truncation);  // now we insert it again as the first hyperplane
112     }
113     SupportHyperplanes.append(M);
114     nr_sh = SupportHyperplanes.nr_of_rows();
115 
116     verbose = false;
117     inhomogeneous = false;
118     do_only_Deg1_Elements = false;
119     truncate = false;
120 
121     Intermediate_HB.dual = true;
122 
123     if (nr_sh != static_cast<size_t>(static_cast<key_t>(nr_sh))) {
124         throw FatalException("Too many support hyperplanes to fit in range of key_t!");
125     }
126 }
127 
128 //---------------------------------------------------------------------------
129 
130 template <typename Integer>
get_support_hyperplanes() const131 Matrix<Integer> Cone_Dual_Mode<Integer>::get_support_hyperplanes() const {
132     return SupportHyperplanes;
133 }
134 
135 //---------------------------------------------------------------------------
136 
137 template <typename Integer>
get_generators() const138 Matrix<Integer> Cone_Dual_Mode<Integer>::get_generators() const {
139     return Generators;
140 }
141 
142 template <typename Integer>
get_extreme_rays() const143 vector<bool> Cone_Dual_Mode<Integer>::get_extreme_rays() const {
144     return ExtremeRaysInd;
145 }
146 
147 // size_t counter=0,counter1=0, counter2=0;
148 
149 //---------------------------------------------------------------------------
150 
151 // In the inhomogeneous case or when only degree 1 elements are to be found,
152 // we truncate the Hilbert basis at level 1. The level is the ordinary degree
153 // for degree 1 elements and the degree of the homogenizing variable
154 // in the inhomogeneous case.
155 //
156 // As soon as there are no positive or neutral (with respect to the current hyperplane)
157 // elements in the current Hilbert basis and truncate==true, new elements can only
158 // be produced as sums of positive irreds of level 1 and negative irreds of level 0.
159 // In particular no new negative elements can be produced, and the only type of
160 // reduction on the positive side is the elimination of duplicates.
161 //
162 // If there are no elements on level 0 at all, then new elements cannot be produced anymore,
163 // and the production of new elements can be skipped.
164 
165 template <typename Integer>
cut_with_halfspace_hilbert_basis(const size_t & hyp_counter,const bool lifting,vector<Integer> & old_lin_subspace_half,bool pointed)166 void Cone_Dual_Mode<Integer>::cut_with_halfspace_hilbert_basis(const size_t& hyp_counter,
167                                                                const bool lifting,
168                                                                vector<Integer>& old_lin_subspace_half,
169                                                                bool pointed) {
170     if (verbose == true) {
171         verboseOutput() << "==================================================" << endl;
172         verboseOutput() << "cut with halfspace " << hyp_counter + 1 << " ..." << endl;
173     }
174 
175     const size_t ReportBound = 100000;
176 
177     size_t i;
178     int sign;
179 
180     CandidateList<Integer> Positive_Irred(true), Negative_Irred(true), Neutral_Irred(true);  // for the Hilbert basis elements
181     Positive_Irred.verbose = Negative_Irred.verbose = Neutral_Irred.verbose = verbose;
182     list<Candidate<Integer>*> Pos_Gen0, Pos_Gen1, Neg_Gen0, Neg_Gen1;  // pointer lists for generation control
183     size_t pos_gen0_size = 0, pos_gen1_size = 0, neg_gen0_size = 0, neg_gen1_size = 0;
184 
185     Integer orientation, scalar_product, factor;
186     vector<Integer> hyperplane = SupportHyperplanes[hyp_counter];  // the current hyperplane dividing the old cone
187 
188     if (lifting == true) {
189         orientation = v_scalar_product<Integer>(hyperplane, old_lin_subspace_half);
190         if (orientation < 0) {
191             orientation = -orientation;
192             v_scalar_multiplication<Integer>(
193                 old_lin_subspace_half, -1);  // transforming into the generator of the positive half of the old max lin subsapce
194         }
195         // from now on orientation > 0
196 
197         for (auto& h :
198              Intermediate_HB.Candidates) {  // reduction  modulo  the generators of the two halves of the old max lin subspace
199             scalar_product = v_scalar_product(hyperplane, h.cand);  //  allows us to declare "old" HB candiadtes as irreducible
200             sign = 1;
201             if (scalar_product < 0) {
202                 scalar_product = -scalar_product;
203                 sign = -1;
204             }
205             factor = scalar_product / orientation;  // we reduce all elements by the generator of the halfspace
206             for (i = 0; i < dim; i++) {
207                 h.cand[i] -= sign * factor * old_lin_subspace_half[i];
208             }
209         }
210 
211         // adding the generators of the halves of the old max lin subspaces to the the "positive" and the "negative" generators
212         // ABSOLUTELY NECESSARY since we need a monoid system of generators of the full "old" cone
213 
214         Candidate<Integer> halfspace_gen_as_cand(old_lin_subspace_half, nr_sh);
215         halfspace_gen_as_cand.mother = 0;
216         // halfspace_gen_as_cand.father=0;
217         halfspace_gen_as_cand.old_tot_deg = 0;
218         (halfspace_gen_as_cand.values)[hyp_counter] = orientation;  // value under the new linear form
219         halfspace_gen_as_cand.sort_deg = convertToLong(orientation);
220         assert(orientation != 0);
221         if (!truncate ||
222             halfspace_gen_as_cand.values[0] <= 1) {           // the only critical case is the positive halfspace gen in round 0
223             Positive_Irred.push_back(halfspace_gen_as_cand);  // it must have value <= 1 under the truncation.
224             Pos_Gen0.push_back(&Positive_Irred.Candidates.front());  //  Later on all these elements have value 0 under it.
225             pos_gen0_size = 1;
226         }
227         v_scalar_multiplication<Integer>(halfspace_gen_as_cand.cand, -1);
228         Negative_Irred.push_back(halfspace_gen_as_cand);
229         Neg_Gen0.push_back(&Negative_Irred.Candidates.front());
230         neg_gen0_size = 1;
231     }  // end lifting
232 
233     long gen0_mindeg;  // minimal degree of a generator
234     if (lifting)
235         gen0_mindeg = 0;  // sort_deg has already been set > 0 for half_space_gen
236     else
237         gen0_mindeg = Intermediate_HB.Candidates.begin()->sort_deg;
238     typename list<Candidate<Integer> >::const_iterator hh;
239     for (const auto& hh : Intermediate_HB.Candidates)
240         if (hh.sort_deg < gen0_mindeg)
241             gen0_mindeg = hh.sort_deg;
242 
243     bool gen1_pos = false, gen1_neg = false;
244     bool no_pos_in_level0 = pointed;
245     bool all_positice_level = pointed;
246     for (auto& h : Intermediate_HB.Candidates) {  // dividing into negative and positive
247         Integer new_val = v_scalar_product<Integer>(hyperplane, h.cand);
248         long new_val_long = convertToLong(new_val);
249         h.reducible = false;
250         h.mother = 0;
251         // h.father=0;
252         h.old_tot_deg = h.sort_deg;
253         if (new_val > 0) {
254             gen1_pos = true;
255             h.values[hyp_counter] = new_val;
256             h.sort_deg += new_val_long;
257             Positive_Irred.Candidates.push_back(h);  // could be spliced
258             Pos_Gen1.push_back(&Positive_Irred.Candidates.back());
259             pos_gen1_size++;
260             if (h.values[0] == 0) {
261                 no_pos_in_level0 = false;
262                 all_positice_level = false;
263             }
264         }
265         if (new_val < 0) {
266             gen1_neg = true;
267             h.values[hyp_counter] = -new_val;
268             h.sort_deg += -new_val_long;
269             Negative_Irred.Candidates.push_back(h);
270             Neg_Gen1.push_back(&Negative_Irred.Candidates.back());
271             neg_gen1_size++;
272             if (h.values[0] == 0) {
273                 all_positice_level = false;
274             }
275         }
276         if (new_val == 0) {
277             Neutral_Irred.Candidates.push_back(h);
278             if (h.values[0] == 0) {
279                 no_pos_in_level0 = false;
280                 all_positice_level = false;
281             }
282         }
283     }
284 
285     if ((truncate && (no_pos_in_level0 && !all_positice_level))) {
286         if (verbose) {
287             verboseOutput() << "Eliminating negative generators of level > 0" << endl;
288         }
289         Neg_Gen1.clear();
290         neg_gen1_size = 0;
291         for (auto h = Negative_Irred.Candidates.begin(); h != Negative_Irred.Candidates.end();) {
292             if (h->values[0] > 0)
293                 h = Negative_Irred.Candidates.erase(h);
294             else {
295                 Neg_Gen1.push_back(&(*h));
296                 neg_gen1_size++;
297                 ++h;
298             }
299         }
300     }
301 
302     std::exception_ptr tmp_exception;
303 
304 #pragma omp parallel num_threads(3)
305     {
306 #pragma omp single nowait
307         {
308             try {
309                 check_range_list(Negative_Irred);
310                 Negative_Irred.sort_by_val();
311                 Negative_Irred.last_hyp = hyp_counter;
312             } catch (const std::exception&) {
313                 tmp_exception = std::current_exception();
314             }
315         }
316 
317 #pragma omp single nowait
318         {
319             try {
320                 check_range_list(Positive_Irred);
321                 Positive_Irred.sort_by_val();
322                 Positive_Irred.last_hyp = hyp_counter;
323             } catch (const std::exception&) {
324                 tmp_exception = std::current_exception();
325             }
326         }
327 
328 #pragma omp single nowait
329         {
330             Neutral_Irred.sort_by_val();
331             Neutral_Irred.last_hyp = hyp_counter;
332         }
333     }
334     if (!(tmp_exception == 0))
335         std::rethrow_exception(tmp_exception);
336 
337     CandidateList<Integer> New_Positive_Irred(true), New_Negative_Irred(true), New_Neutral_Irred(true);
338     New_Positive_Irred.verbose = New_Negative_Irred.verbose = New_Neutral_Irred.verbose = verbose;
339     New_Negative_Irred.last_hyp = hyp_counter;  // for the newly generated vector in each thread
340     New_Positive_Irred.last_hyp = hyp_counter;
341     New_Neutral_Irred.last_hyp = hyp_counter;
342 
343     CandidateList<Integer> Positive_Depot(true), Negative_Depot(true),
344         Neutral_Depot(true);  // to store the new vectors after generation
345     Positive_Depot.verbose = Negative_Depot.verbose = Neutral_Depot.verbose = verbose;
346 
347     vector<CandidateList<Integer> > New_Positive_thread(omp_get_max_threads()), New_Negative_thread(omp_get_max_threads()),
348         New_Neutral_thread(omp_get_max_threads());
349 
350     vector<CandidateTable<Integer> > Pos_Table, Neg_Table, Neutr_Table;  // for reduction in each thread
351 
352     for (long i = 0; i < omp_get_max_threads(); ++i) {
353         New_Positive_thread[i].dual = true;
354         New_Positive_thread[i].verbose = verbose;
355         New_Negative_thread[i].dual = true;
356         New_Negative_thread[i].verbose = verbose;
357         New_Neutral_thread[i].dual = true;
358         New_Neutral_thread[i].verbose = verbose;
359     }
360 
361     for (int k = 0; k < omp_get_max_threads(); ++k) {
362         Pos_Table.push_back(CandidateTable<Integer>(Positive_Irred));
363         Neg_Table.push_back(CandidateTable<Integer>(Negative_Irred));
364         Neutr_Table.push_back(CandidateTable<Integer>(Neutral_Irred));
365     }
366 
367     bool not_done;
368     if (lifting)
369         not_done = gen1_pos || gen1_neg;
370     else
371         not_done = gen1_pos && gen1_neg;
372 
373     bool do_reduction = !(truncate && no_pos_in_level0);
374 
375     bool do_only_selection = truncate && all_positice_level;
376 
377     size_t round = 0;
378 
379     if (do_only_selection) {
380         pos_gen0_size = pos_gen1_size;  // otherwise wrong sizes in message at the end
381         neg_gen0_size = neg_gen1_size;
382     }
383 
384     while (not_done && !do_only_selection) {
385         // generating new elements
386         round++;
387 
388         typename list<Candidate<Integer>*>::iterator pos_begin, pos_end, neg_begin, neg_end;
389         size_t pos_size, neg_size;
390 
391         // Steps are:
392         // 0: old pos vs. new neg
393         // 1: new pos vs. old neg
394         // 2: new pos vs. new neg
395         for (size_t step = 0; step <= 2; step++) {
396             if (step == 0) {
397                 pos_begin = Pos_Gen0.begin();
398                 pos_end = Pos_Gen0.end();
399                 neg_begin = Neg_Gen1.begin();
400                 neg_end = Neg_Gen1.end();
401                 pos_size = pos_gen0_size;
402                 neg_size = neg_gen1_size;
403             }
404 
405             if (step == 1) {
406                 pos_begin = Pos_Gen1.begin();
407                 pos_end = Pos_Gen1.end();
408                 neg_begin = Neg_Gen0.begin();
409                 neg_end = Neg_Gen0.end();
410                 pos_size = pos_gen1_size;
411                 neg_size = neg_gen0_size;
412                 ;
413             }
414 
415             if (step == 2) {
416                 pos_begin = Pos_Gen1.begin();
417                 pos_end = Pos_Gen1.end();
418                 neg_begin = Neg_Gen1.begin();
419                 neg_end = Neg_Gen1.end();
420                 pos_size = pos_gen1_size;
421                 neg_size = neg_gen1_size;
422             }
423 
424             if (pos_size == 0 || neg_size == 0)
425                 continue;
426 
427             vector<typename list<Candidate<Integer>*>::iterator> PosBlockStart, NegBlockStart;
428 
429             const size_t Blocksize = 200;
430 
431             size_t nr_in_block = 0, pos_block_nr = 0;
432             for (auto p = pos_begin; p != pos_end; ++p) {
433                 if (nr_in_block % Blocksize == 0) {
434                     PosBlockStart.push_back(p);
435                     pos_block_nr++;
436                     nr_in_block = 0;
437                 }
438                 nr_in_block++;
439             }
440             PosBlockStart.push_back(pos_end);
441 
442             nr_in_block = 0;
443             size_t neg_block_nr = 0;
444             for (auto n = neg_begin; n != neg_end; ++n) {
445                 if (nr_in_block % Blocksize == 0) {
446                     NegBlockStart.push_back(n);
447                     neg_block_nr++;
448                     nr_in_block = 0;
449                 }
450                 nr_in_block++;
451             }
452             NegBlockStart.push_back(neg_end);
453 
454             // cout << "Step " << step << " pos " << pos_size << " neg " << neg_size << endl;
455 
456             if (verbose) {
457                 // size_t neg_size=Negative_Irred.size();
458                 // size_t zsize=Neutral_Irred.size();
459                 if (pos_size * neg_size >= ReportBound)
460                     verboseOutput() << "Positive: " << pos_size << "  Negative: " << neg_size << endl;
461                 else {
462                     if (round % 100 == 0)
463                         verboseOutput() << "Round " << round << endl;
464                 }
465             }
466 
467             bool skip_remaining = false;
468 
469             const long VERBOSE_STEPS = 50;
470             long step_x_size = pos_block_nr * neg_block_nr - VERBOSE_STEPS;
471 
472 #pragma omp parallel
473             {
474                 Candidate<Integer> new_candidate(dim, nr_sh);
475 
476                 size_t total = pos_block_nr * neg_block_nr;
477 
478 #pragma omp for schedule(dynamic)
479                 for (size_t bb = 0; bb < total; ++bb) {  // main loop over the blocks
480 
481                     if (skip_remaining)
482                         continue;
483 
484                     try {
485                         INTERRUPT_COMPUTATION_BY_EXCEPTION
486 
487                         if (verbose && pos_size * neg_size >= ReportBound) {
488 #pragma omp critical(VERBOSE)
489                             while ((long)(bb * VERBOSE_STEPS) >= step_x_size) {
490                                 step_x_size += total;
491                                 verboseOutput() << "." << flush;
492                             }
493                         }
494 
495                         size_t nr_pos = bb / neg_block_nr;
496                         size_t nr_neg = bb % neg_block_nr;
497 
498                         for (auto p = PosBlockStart[nr_pos]; p != PosBlockStart[nr_pos + 1]; ++p) {
499                             Candidate<Integer>* p_cand = *p;
500 
501                             Integer pos_val = p_cand->values[hyp_counter];
502 
503                             for (auto n = NegBlockStart[nr_neg]; n != NegBlockStart[nr_neg + 1]; ++n) {
504                                 Candidate<Integer>* n_cand = *n;
505 
506                                 if (truncate && p_cand->values[0] + n_cand->values[0] >=
507                                                     2)  // in the inhomogeneous case we truncate at level 1
508                                     continue;
509 
510                                 Integer neg_val = n_cand->values[hyp_counter];
511                                 Integer diff = pos_val - neg_val;
512 
513                                 // prediction of reducibility
514 
515                                 if (diff > 0 && n_cand->mother != 0 &&
516                                     (n_cand->mother <= pos_val  // sum of p_cand and n_cand would be irreducible by mother + the
517                                                                 // vector on the opposite side
518                                      || (p_cand->mother >= n_cand->mother &&
519                                          p_cand->mother - n_cand->mother <= diff)  // sum would reducible ny mother + mother
520                                      )) {
521                                     // #pragma omp atomic
522                                     // counter1++;
523                                     continue;
524                                 }
525 
526                                 if (diff < 0 && p_cand->mother != 0 &&
527                                     (p_cand->mother <= neg_val ||
528                                      (n_cand->mother >= p_cand->mother && n_cand->mother - p_cand->mother <= -diff))) {
529                                     // #pragma omp atomic     // sum would be irreducible by mother + the vector on the opposite
530                                     // side counter1++;
531                                     continue;
532                                 }
533 
534                                 if (diff == 0 && p_cand->mother != 0 && n_cand->mother == p_cand->mother) {
535                                     // #pragma omp atomic
536                                     // counter1++;
537                                     continue;
538                                 }
539 
540                                 // #pragma omp atomic
541                                 // counter++;
542 
543                                 new_candidate.old_tot_deg = p_cand->old_tot_deg + n_cand->old_tot_deg;
544                                 v_add_result(new_candidate.values, hyp_counter, p_cand->values,
545                                              n_cand->values);  // new_candidate=v_add
546 
547                                 if (diff > 0) {
548                                     new_candidate.values[hyp_counter] = diff;
549                                     new_candidate.sort_deg = p_cand->sort_deg + n_cand->sort_deg - 2 * convertToLong(neg_val);
550                                     if (do_reduction && (Pos_Table[omp_get_thread_num()].is_reducible_unordered(new_candidate) ||
551                                                          Neutr_Table[omp_get_thread_num()].is_reducible_unordered(new_candidate)))
552                                         continue;
553                                     v_add_result(new_candidate.cand, dim, p_cand->cand, n_cand->cand);
554                                     new_candidate.mother = pos_val;
555                                     // new_candidate.father=neg_val;
556                                     New_Positive_thread[omp_get_thread_num()].push_back(new_candidate);
557                                 }
558                                 if (diff < 0) {
559                                     if (!do_reduction)  // don't need new negative elements anymore
560                                         continue;
561                                     new_candidate.values[hyp_counter] = -diff;
562                                     new_candidate.sort_deg = p_cand->sort_deg + n_cand->sort_deg - 2 * convertToLong(pos_val);
563                                     if (Neg_Table[omp_get_thread_num()].is_reducible_unordered(new_candidate)) {
564                                         continue;
565                                     }
566                                     if (Neutr_Table[omp_get_thread_num()].is_reducible_unordered(new_candidate)) {
567                                         continue;
568                                     }
569                                     v_add_result(new_candidate.cand, dim, p_cand->cand, n_cand->cand);
570                                     new_candidate.mother = neg_val;
571                                     // new_candidate.father=pos_val;
572                                     New_Negative_thread[omp_get_thread_num()].push_back(new_candidate);
573                                 }
574                                 if (diff == 0) {
575                                     new_candidate.values[hyp_counter] = 0;
576                                     new_candidate.sort_deg =
577                                         p_cand->sort_deg + n_cand->sort_deg - 2 * convertToLong(pos_val);  // pos_val==neg_val
578                                     if (do_reduction && Neutr_Table[omp_get_thread_num()].is_reducible_unordered(new_candidate)) {
579                                         continue;
580                                     }
581                                     v_add_result(new_candidate.cand, dim, p_cand->cand, n_cand->cand);
582                                     new_candidate.mother = 0;  // irrelevant, but we define it
583                                     New_Neutral_thread[omp_get_thread_num()].push_back(new_candidate);
584                                 }
585                             }  // neg
586 
587                         }  // pos
588 
589                     } catch (const std::exception&) {
590                         tmp_exception = std::current_exception();
591                         skip_remaining = true;
592 #pragma omp flush(skip_remaining)
593                     }
594 
595                 }  // bb, end generation of new elements
596 
597 #pragma omp single
598                 {
599                     if (verbose && pos_size * neg_size >= ReportBound)
600                         verboseOutput() << endl;
601                 }
602 
603             }  // END PARALLEL
604 
605             if (!(tmp_exception == 0))
606                 std::rethrow_exception(tmp_exception);
607 
608         }  // steps
609 
610         Pos_Gen0.splice(Pos_Gen0.end(), Pos_Gen1);  // the new generation has become old
611         pos_gen0_size += pos_gen1_size;
612         pos_gen1_size = 0;
613         Neg_Gen0.splice(Neg_Gen0.end(), Neg_Gen1);
614         neg_gen0_size += neg_gen1_size;
615         neg_gen1_size = 0;
616 
617         splice_them_sort(Neutral_Depot, New_Neutral_thread);  // sort by sort_deg and values
618 
619         splice_them_sort(Positive_Depot, New_Positive_thread);
620 
621         splice_them_sort(Negative_Depot, New_Negative_thread);
622 
623         if (Positive_Depot.empty() && Negative_Depot.empty())
624             not_done = false;
625 
626         // Attention: the element with smallest old_tot_deg need not be the first in the list which is ordered by sort_deg
627         size_t gen1_mindeg = 0;  // minimal old_tot_deg of a new element used for generation
628         bool first = true;
629         for (const auto& c : Positive_Depot.Candidates) {
630             if (first) {
631                 first = false;
632                 gen1_mindeg = c.old_tot_deg;
633             }
634             if (c.old_tot_deg < gen1_mindeg)
635                 gen1_mindeg = c.old_tot_deg;
636         }
637 
638         for (const auto& c : Negative_Depot.Candidates) {
639             if (first) {
640                 first = false;
641                 gen1_mindeg = c.old_tot_deg;
642             }
643             if (c.old_tot_deg < gen1_mindeg)
644                 gen1_mindeg = c.old_tot_deg;
645         }
646 
647         size_t min_deg_new = gen0_mindeg + gen1_mindeg;
648         if (not_done)
649             assert(min_deg_new > 0);
650 
651         size_t all_known_deg = min_deg_new - 1;
652         size_t guaranteed_HB_deg =
653             2 * all_known_deg + 1;  // the degree up to which we can decide whether an element belongs to the HB
654 
655         if (not_done) {
656             select_HB(Neutral_Depot, guaranteed_HB_deg, New_Neutral_Irred, !do_reduction);
657         }
658         else {
659             Neutral_Depot.auto_reduce_sorted();         // in this case new elements will not be produced anymore
660             Neutral_Irred.merge_by_val(Neutral_Depot);  // and there is nothing to do for positive or negative elements
661                                                         // but the remaining neutral elements must be auto-reduced.
662         }
663 
664         CandidateTable<Integer> New_Pos_Table(true, hyp_counter), New_Neg_Table(true, hyp_counter),
665             New_Neutr_Table(true, hyp_counter);
666         // for new elements
667 
668         if (!New_Neutral_Irred.empty()) {
669             if (do_reduction) {
670                 Positive_Depot.reduce_by(New_Neutral_Irred);
671                 Neutral_Depot.reduce_by(New_Neutral_Irred);
672             }
673             Negative_Depot.reduce_by(New_Neutral_Irred);
674             list<Candidate<Integer>*> New_Elements;
675             Neutral_Irred.merge_by_val(New_Neutral_Irred, New_Elements);
676             for (const auto& c : New_Elements) {
677                 New_Neutr_Table.ValPointers.push_back(pair<size_t, vector<Integer>*>(c->sort_deg, &(c->values)));
678             }
679             New_Elements.clear();
680         }
681 
682         select_HB(Positive_Depot, guaranteed_HB_deg, New_Positive_Irred, !do_reduction);
683 
684         select_HB(Negative_Depot, guaranteed_HB_deg, New_Negative_Irred, !do_reduction);
685 
686         if (!New_Positive_Irred.empty()) {
687             if (do_reduction)
688                 Positive_Depot.reduce_by(New_Positive_Irred);
689             check_range_list(New_Positive_Irred);  // check for danger of overflow
690             Positive_Irred.merge_by_val(New_Positive_Irred, Pos_Gen1);
691             for (const auto& c : Pos_Gen1) {
692                 New_Pos_Table.ValPointers.push_back(pair<size_t, vector<Integer>*>(c->sort_deg, &(c->values)));
693                 pos_gen1_size++;
694             }
695         }
696 
697         if (!New_Negative_Irred.empty()) {
698             Negative_Depot.reduce_by(New_Negative_Irred);
699             check_range_list(New_Negative_Irred);
700             Negative_Irred.merge_by_val(New_Negative_Irred, Neg_Gen1);
701             for (const auto& c : Neg_Gen1) {
702                 New_Neg_Table.ValPointers.push_back(pair<size_t, vector<Integer>*>(c->sort_deg, &(c->values)));
703                 neg_gen1_size++;
704             }
705         }
706 
707         CandidateTable<Integer> Help(true, hyp_counter);
708 
709         for (int k = 0; k < omp_get_max_threads(); ++k) {
710             Help = New_Pos_Table;
711             Pos_Table[k].ValPointers.splice(Pos_Table[k].ValPointers.end(), Help.ValPointers);
712             Help = New_Neg_Table;
713             Neg_Table[k].ValPointers.splice(Neg_Table[k].ValPointers.end(), Help.ValPointers);
714             Help = New_Neutr_Table;
715             Neutr_Table[k].ValPointers.splice(Neutr_Table[k].ValPointers.end(), Help.ValPointers);
716         }
717 
718     }  // while(not_done)
719 
720     if (verbose) {
721         verboseOutput() << "Final sizes: Pos " << pos_gen0_size << " Neg " << neg_gen0_size << " Neutral " << Neutral_Irred.size()
722                         << endl;
723     }
724 
725     Intermediate_HB.clear();
726     Intermediate_HB.Candidates.splice(Intermediate_HB.Candidates.begin(), Positive_Irred.Candidates);
727     Intermediate_HB.Candidates.splice(Intermediate_HB.Candidates.end(), Neutral_Irred.Candidates);
728     Intermediate_HB.sort_by_val();
729 }
730 
731 //---------------------------------------------------------------------------
732 
733 template <typename Integer>
cut_with_halfspace(const size_t & hyp_counter,const Matrix<Integer> & BasisMaxSubspace)734 Matrix<Integer> Cone_Dual_Mode<Integer>::cut_with_halfspace(const size_t& hyp_counter, const Matrix<Integer>& BasisMaxSubspace) {
735     INTERRUPT_COMPUTATION_BY_EXCEPTION
736 
737     size_t i, rank_subspace = BasisMaxSubspace.nr_of_rows();
738 
739     vector<Integer> restriction, lin_form = SupportHyperplanes[hyp_counter], old_lin_subspace_half;
740     bool lifting = false;
741     Matrix<Integer> New_BasisMaxSubspace =
742         BasisMaxSubspace;  // the new maximal subspace is the intersection of the old with the new haperplane
743     if (rank_subspace != 0) {
744         restriction = BasisMaxSubspace.MxV(lin_form);  // the restriction of the new linear form to Max_Subspace
745         for (i = 0; i < rank_subspace; i++)
746             if (restriction[i] != 0)
747                 break;
748         if (i != rank_subspace) {  // the new hyperplane does not contain the intersection of the previous hyperplanes
749                                    // so we must intersect the new hyperplane and Max_Subspace
750             lifting = true;
751 
752             Matrix<Integer> M(1, rank_subspace);  // this is the restriction of the new linear form to Max_Subspace
753             M[0] = restriction;                   // encoded as a matrix
754 
755             size_t dummy_rank;
756             Matrix<Integer> NewBasisOldMaxSubspace =
757                 M.AlmostHermite(dummy_rank).transpose();  // compute kernel of restriction and complementary subspace
758 
759             Matrix<Integer> NewBasisOldMaxSubspaceAmbient = NewBasisOldMaxSubspace.multiplication(BasisMaxSubspace);
760             // in coordinates of the ambient space
761 
762             old_lin_subspace_half = NewBasisOldMaxSubspaceAmbient[0];
763 
764             // old_lin_subspace_half refers to the fact that the complementary space is subdivided into
765             // two halfspaces generated by old_lin_subspace_half and -old_lin_subspace_half (taken care of in
766             // cut_with_halfspace_hilbert_basis)
767 
768             Matrix<Integer> temp(rank_subspace - 1, dim);
769             for (size_t k = 1; k < rank_subspace; ++k)
770                 temp[k - 1] = NewBasisOldMaxSubspaceAmbient[k];
771             New_BasisMaxSubspace = temp;
772         }
773     }
774     bool pointed = (BasisMaxSubspace.nr_of_rows() == 0);
775 
776     cut_with_halfspace_hilbert_basis(hyp_counter, lifting, old_lin_subspace_half, pointed);
777 
778     return New_BasisMaxSubspace;
779 }
780 
781 //---------------------------------------------------------------------------
782 
783 template <typename Integer>
hilbert_basis_dual()784 void Cone_Dual_Mode<Integer>::hilbert_basis_dual() {
785     truncate = inhomogeneous || do_only_Deg1_Elements;
786 
787     if (dim == 0)
788         return;
789 
790     if (verbose == true) {
791         verboseOutput() << "************************************************************\n";
792         verboseOutput() << "computing Hilbert basis";
793         if (truncate)
794             verboseOutput() << " (truncated)";
795         verboseOutput() << " ..." << endl;
796     }
797 
798     if (Generators.nr_of_rows() != ExtremeRaysInd.size()) {
799         throw FatalException("Mismatch of extreme rays and generators in cone dual mode. THIS SHOULD NOT HAPPEN.");
800     }
801 
802     size_t hyp_counter;  // current hyperplane
803     for (hyp_counter = 0; hyp_counter < nr_sh; hyp_counter++) {
804         BasisMaxSubspace = cut_with_halfspace(hyp_counter, BasisMaxSubspace);
805     }
806 
807     if (ExtremeRaysInd.size() > 0) {  // implies that we have transformed everything to a pointed full-dimensional cone
808         // must produce the relevant support hyperplanes from the generators
809         // since the Hilbert basis may have been truncated
810         vector<Integer> test(SupportHyperplanes.nr_of_rows());
811         vector<key_t> key;
812         vector<key_t> relevant_sh;
813         size_t realdim = Generators.rank();
814         for (key_t h = 0; h < SupportHyperplanes.nr_of_rows(); ++h) {
815             INTERRUPT_COMPUTATION_BY_EXCEPTION
816 
817             key.clear();
818             vector<Integer> test = Generators.MxV(SupportHyperplanes[h]);
819             for (key_t i = 0; i < test.size(); ++i)
820                 if (test[i] == 0)
821                     key.push_back(i);
822             if (key.size() >= realdim - 1 && Generators.submatrix(key).rank() >= realdim - 1)
823                 relevant_sh.push_back(h);
824         }
825         SupportHyperplanes = SupportHyperplanes.submatrix(relevant_sh);
826     }
827     if (!truncate && ExtremeRaysInd.size() == 0) {  // no precomputed generators
828         extreme_rays_rank();
829         relevant_support_hyperplanes();
830         ExtremeRayList.clear();
831     }
832 
833     /* if(verbose)
834        verboseOutput() << "matches = " << counter << endl << "avoided = " << counter1 << endl <<
835             "comparisons = " << redcounter << endl << "comp/match " << (float) redcounter/(float) counter << endl;
836        // verboseOutput() << "matches = " << counter << endl << "avoided = " << counter1 << endl; //  << "add avoided " <<
837        counter2 << endl;
838     */
839 
840     Intermediate_HB.extract(Hilbert_Basis);
841 
842     if (verbose) {
843         verboseOutput() << "Hilbert basis ";
844         if (truncate)
845             verboseOutput() << "(truncated) ";
846         verboseOutput() << Hilbert_Basis.size() << endl;
847     }
848     if (SupportHyperplanes.nr_of_rows() > 0 && inhomogeneous)
849         v_make_prime(SupportHyperplanes[0]);  // it could be that the truncation was not coprime
850 }
851 
852 //---------------------------------------------------------------------------
853 
854 template <typename Integer>
extreme_rays_rank()855 void Cone_Dual_Mode<Integer>::extreme_rays_rank() {
856     if (verbose) {
857         verboseOutput() << "Find extreme rays" << endl;
858     }
859     size_t quotient_dim = dim - BasisMaxSubspace.nr_of_rows();
860 
861     vector<key_t> zero_list;
862     size_t i, k;
863     for (auto& c : Intermediate_HB.Candidates) {
864         INTERRUPT_COMPUTATION_BY_EXCEPTION
865 
866         zero_list.clear();
867         for (i = 0; i < nr_sh; i++) {
868             if (c.values[i] == 0) {
869                 zero_list.push_back(i);
870             }
871         }
872         k = zero_list.size();
873         if (k >= quotient_dim - 1) {
874             // Matrix<Integer> Test=SupportHyperplanes.submatrix(zero_list);
875             if (SupportHyperplanes.rank_submatrix(zero_list) >= quotient_dim - 1) {
876                 ExtremeRayList.push_back(&c);
877             }
878         }
879     }
880     size_t s = ExtremeRayList.size();
881     // cout << "nr extreme " << s << endl;
882     Generators = Matrix<Integer>(s, dim);
883 
884     i = 0;
885     for (const auto& l : ExtremeRayList) {
886         Generators[i++] = l->cand;
887     }
888     ExtremeRaysInd = vector<bool>(s, true);
889 }
890 
891 //---------------------------------------------------------------------------
892 
893 template <typename Integer>
relevant_support_hyperplanes()894 void Cone_Dual_Mode<Integer>::relevant_support_hyperplanes() {
895     if (verbose) {
896         verboseOutput() << "Find relevant support hyperplanes" << endl;
897     }
898     size_t i, k, k1;
899 
900     // size_t realdim = Generators.rank();
901 
902     vector<dynamic_bitset> ind(nr_sh, dynamic_bitset(ExtremeRayList.size()));
903     dynamic_bitset relevant(nr_sh);
904     relevant.set();
905 
906     for (i = 0; i < nr_sh; ++i) {
907         INTERRUPT_COMPUTATION_BY_EXCEPTION
908 
909         k = 0;
910         k1 = 0;
911         for (const auto& gen_it : ExtremeRayList) {
912             if (gen_it->values[i] == 0) {
913                 ind[i][k] = true;
914                 k1++;
915             }
916             k++;
917         }
918         if (/* k1<realdim-1 || */ k1 == Generators.nr_of_rows()) {  // discard everything that vanishes on the cone
919             relevant[i] = false;
920         }
921     }
922     maximal_subsets(ind, relevant);
923     SupportHyperplanes = SupportHyperplanes.submatrix(bitset_to_bool(relevant));
924 }
925 
926 //---------------------------------------------------------------------------
927 
928 template <typename Integer>
to_sublattice(const Sublattice_Representation<Integer> & SR)929 void Cone_Dual_Mode<Integer>::to_sublattice(const Sublattice_Representation<Integer>& SR) {
930     assert(SR.getDim() == dim);
931 
932     if (SR.IsIdentity())
933         return;
934 
935     dim = SR.getRank();
936     SupportHyperplanes = SR.to_sublattice_dual(SupportHyperplanes);
937 
938     Generators = SR.to_sublattice(Generators);
939     BasisMaxSubspace = SR.to_sublattice(BasisMaxSubspace);
940 
941     for (auto it = Hilbert_Basis.begin(); it != Hilbert_Basis.end();) {
942         auto tmp = SR.to_sublattice(*it);
943         it = Hilbert_Basis.erase(it);
944         Hilbert_Basis.insert(it, tmp);
945     }
946 }
947 
948 #ifndef NMZ_MIC_OFFLOAD  // offload with long is not supported
949 template class Cone_Dual_Mode<long>;
950 #endif
951 template class Cone_Dual_Mode<long long>;
952 template class Cone_Dual_Mode<mpz_class>;
953 
954 }  // end namespace libnormaliz
955