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