1 /*++
2 Copyright (c) 2013 Microsoft Corporation
3
4 Module Name:
5
6 hilbert_basis.cpp
7
8 Abstract:
9
10 Basic Hilbert Basis computation.
11
12 Author:
13
14 Nikolaj Bjorner (nbjorner) 2013-02-09.
15
16 Revision History:
17
18 --*/
19
20 #include "math/hilbert/hilbert_basis.h"
21 #include "util/heap.h"
22 #include "util/map.h"
23 #include "math/hilbert/heap_trie.h"
24 #include "util/stopwatch.h"
25
26
27 typedef int_hashtable<int_hash, default_eq<int> > int_table;
28
29
30 class hilbert_basis::value_index1 {
31 struct stats {
32 unsigned m_num_comparisons;
33 unsigned m_num_hit;
34 unsigned m_num_miss;
35
statshilbert_basis::value_index1::stats36 stats() { reset(); }
resethilbert_basis::value_index1::stats37 void reset() { memset(this, 0, sizeof(*this)); }
38 };
39
40 hilbert_basis& hb;
41 int_table m_table;
42 stats m_stats;
43
44 public:
value_index1(hilbert_basis & hb)45 value_index1(hilbert_basis& hb):
46 hb(hb)
47 {}
48
insert(offset_t idx,values const & vs)49 void insert(offset_t idx, values const& vs) {
50 m_table.insert(idx.m_offset);
51 }
52
remove(offset_t idx,values const & vs)53 void remove(offset_t idx, values const& vs) {
54 m_table.remove(idx.m_offset);
55 }
56
reset()57 void reset() {
58 m_table.reset();
59 }
60
find(offset_t idx,values const & vs)61 bool find(offset_t idx, values const& vs) {
62 // display_profile(idx, std::cout);
63 int_table::iterator it = m_table.begin(), end = m_table.end();
64 for (; it != end; ++it) {
65 offset_t offs(*it);
66 ++m_stats.m_num_comparisons;
67 if (*it != static_cast<int>(idx.m_offset) && hb.is_subsumed(idx, offs)) {
68 ++m_stats.m_num_hit;
69 return true;
70 }
71 }
72 ++m_stats.m_num_miss;
73 return false;
74 }
75
collect_statistics(statistics & st) const76 void collect_statistics(statistics& st) const {
77 st.update("hb.index.num_comparisons", m_stats.m_num_comparisons);
78 st.update("hb.index.num_hit", m_stats.m_num_hit);
79 st.update("hb.index.num_miss", m_stats.m_num_miss);
80 }
81
reset_statistics()82 void reset_statistics() {
83 m_stats.reset();
84 }
85
size() const86 unsigned size() const {
87 return m_table.size();
88 }
89
display(std::ostream & out) const90 void display(std::ostream& out) const {
91 int_table::iterator it = m_table.begin(), end = m_table.end();
92 for (; it != end; ++it) {
93 offset_t offs(*it);
94 hb.display(out, offs);
95 }
96 display_profile(out);
97 }
98
99 private:
100
101 typedef hashtable<numeral, numeral::hash_proc, numeral::eq_proc> numeral_set;
102
display_profile(std::ostream & out) const103 void display_profile(std::ostream& out) const {
104 vector<numeral_set> weights;
105 weights.resize(hb.get_num_vars()+1);
106 int_table::iterator it = m_table.begin(), end = m_table.end();
107 for (; it != end; ++it) {
108 offset_t offs(*it);
109 values const& ws = hb.vec(offs);
110 weights[0].insert(ws.weight());
111 for (unsigned i = 0; i < hb.get_num_vars(); ++i) {
112 weights[i+1].insert(ws[i]);
113 }
114 }
115 out << "profile: ";
116 for (unsigned i = 0; i < weights.size(); ++i) {
117 out << weights[i].size() << " ";
118 }
119 out << "\n";
120 }
121
display_profile(offset_t idx,std::ostream & out)122 void display_profile(offset_t idx, std::ostream& out) {
123 unsigned_vector leq;
124 unsigned nv = hb.get_num_vars();
125 values const& vs = hb.vec(idx);
126 leq.resize(nv+1);
127 numeral maxw(0);
128 for (unsigned i = 0; i < nv; ++i) {
129 if (!hb.is_abs_geq(maxw, vs[i])) {
130 maxw = vs[i];
131 }
132 }
133 unsigned num_below_max = 0;
134 int_table::iterator it = m_table.begin(), end = m_table.end();
135 for (; it != end; ++it) {
136 offset_t offs(*it);
137 values const& ws = hb.vec(offs);
138 if (ws.weight() <= vs.weight()) {
139 leq[0]++;
140 }
141 bool filtered = false;
142 for (unsigned i = 0; i < nv; ++i) {
143 if (hb.is_abs_geq(vs[i], ws[i])) {
144 leq[i+1]++;
145 }
146 if (!hb.is_abs_geq(maxw, ws[i])) {
147 filtered = true;
148 }
149 }
150 if (!filtered) {
151 ++num_below_max;
152 }
153 }
154 out << vs.weight() << ":" << leq[0] << " ";
155 for (unsigned i = 0; i < nv; ++i) {
156 out << vs[i] << ":" << leq[i+1] << " ";
157 }
158 out << " max<= " << num_below_max;
159 out << "\n";
160 }
161 };
162
163 class hilbert_basis::value_index2 {
164 struct key_le {
165 hilbert_basis& hb;
key_lehilbert_basis::value_index2::key_le166 key_le(hilbert_basis& hb): hb(hb) {}
lehilbert_basis::value_index2::key_le167 bool le(numeral const& n1, numeral const& n2) const {
168 return hb.is_abs_geq(n2, n1);
169 }
170 };
171
172 typedef heap_trie<numeral, key_le, numeral::hash_proc, unsigned> ht;
173
174 struct checker : public ht::check_value {
175 hilbert_basis* hb;
176 offset_t m_value;
checkerhilbert_basis::value_index2::checker177 checker(): hb(nullptr) {}
operator ()hilbert_basis::value_index2::checker178 bool operator()(unsigned const& v) override {
179 if (m_value.m_offset != v) { // && hb->is_subsumed(m_value, offset_t(v))) {
180 return true;
181 }
182 else {
183 return false;
184 }
185 }
186 };
187 hilbert_basis& hb;
188 key_le m_le;
189 ht m_trie;
190 checker m_checker;
191 unsigned m_offset;
192
get_keys(values const & vs)193 numeral const* get_keys(values const& vs) {
194 return vs()-m_offset;
195 }
196
197 public:
value_index2(hilbert_basis & hb)198 value_index2(hilbert_basis& hb):
199 hb(hb),
200 m_le(hb),
201 m_trie(m_le),
202 m_offset(1) {
203 m_checker.hb = &hb;
204 }
205
insert(offset_t idx,values const & vs)206 void insert(offset_t idx, values const& vs) {
207 m_trie.insert(get_keys(vs), idx.m_offset);
208 }
209
remove(offset_t idx,values const & vs)210 void remove(offset_t idx, values const& vs) {
211 m_trie.remove(get_keys(vs));
212 }
213
reset(unsigned offset)214 void reset(unsigned offset) {
215 m_offset = offset;
216 m_trie.reset(hb.get_num_vars()+m_offset);
217 }
218
find(offset_t idx,values const & vs)219 bool find(offset_t idx, values const& vs) {
220 m_checker.m_value = idx;
221 return m_trie.find_le(get_keys(vs), m_checker);
222 }
223
collect_statistics(statistics & st) const224 void collect_statistics(statistics& st) const {
225 m_trie.collect_statistics(st);
226 }
227
reset_statistics()228 void reset_statistics() {
229 m_trie.reset_statistics();
230 }
231
size() const232 unsigned size() const {
233 return m_trie.size();
234 }
235
display(std::ostream & out) const236 void display(std::ostream& out) const {
237 // m_trie.display(out);
238 }
239 };
240
241
242
243 class hilbert_basis::index {
244 // for each non-positive weight a separate index.
245 // for positive weights a shared value index.
246
247 // typedef value_index1 value_index;
248 typedef value_index2 value_index;
249 // typedef value_index3 value_index;
250
251 struct stats {
252 unsigned m_num_find;
253 unsigned m_num_insert;
statshilbert_basis::index::stats254 stats() { reset(); }
resethilbert_basis::index::stats255 void reset() { memset(this, 0, sizeof(*this)); }
256 };
257
258 template<typename Value>
259 class numeral_map : public map<numeral, Value, numeral::hash_proc, numeral::eq_proc> {};
260
261 typedef numeral_map<value_index*> value_map;
262 hilbert_basis& hb;
263 value_map m_neg;
264 value_index m_pos;
265 value_index m_zero;
266 stats m_stats;
267 unsigned m_num_ineqs;
268
269 public:
index(hilbert_basis & hb)270 index(hilbert_basis& hb): hb(hb), m_pos(hb), m_zero(hb), m_num_ineqs(0) {}
271
insert(offset_t idx,values const & vs)272 void insert(offset_t idx, values const& vs) {
273 ++m_stats.m_num_insert;
274 if (vs.weight().is_pos()) {
275 m_pos.insert(idx, vs);
276 }
277 else if (vs.weight().is_zero()) {
278 m_zero.insert(idx, vs);
279 }
280 else {
281 value_index* map = nullptr;
282 if (!m_neg.find(vs.weight(), map)) {
283 map = alloc(value_index, hb);
284 map->reset(m_num_ineqs);
285 m_neg.insert(vs.weight(), map);
286 }
287 map->insert(idx, vs);
288 }
289 }
290
remove(offset_t idx,values const & vs)291 void remove(offset_t idx, values const& vs) {
292 if (vs.weight().is_pos()) {
293 m_pos.remove(idx, vs);
294 }
295 else if (vs.weight().is_zero()) {
296 m_zero.remove(idx, vs);
297 }
298 else {
299 m_neg.find(vs.weight())->remove(idx, vs);
300 }
301 }
302
find(offset_t idx,values const & vs)303 bool find(offset_t idx, values const& vs) {
304 ++m_stats.m_num_find;
305 if (vs.weight().is_pos()) {
306 return m_pos.find(idx, vs);
307 }
308 else if (vs.weight().is_zero()) {
309 return m_zero.find(idx, vs);
310 }
311 else {
312 value_index* map;
313 return
314 m_neg.find(vs.weight(), map) &&
315 map->find(idx, vs);
316 }
317 }
318
reset(unsigned num_ineqs)319 void reset(unsigned num_ineqs) {
320 value_map::iterator it = m_neg.begin(), end = m_neg.end();
321 for (; it != end; ++it) {
322 dealloc(it->m_value);
323 }
324 m_pos.reset(num_ineqs);
325 m_zero.reset(num_ineqs);
326 m_num_ineqs = num_ineqs;
327 m_neg.reset();
328 }
329
collect_statistics(statistics & st) const330 void collect_statistics(statistics& st) const {
331 m_pos.collect_statistics(st);
332 m_zero.collect_statistics(st);
333 value_map::iterator it = m_neg.begin(), end = m_neg.end();
334 for (; it != end; ++it) {
335 it->m_value->collect_statistics(st);
336 }
337 st.update("hb.index.num_find", m_stats.m_num_find);
338 st.update("hb.index.num_insert", m_stats.m_num_insert);
339 st.update("hb.index.size", size());
340 }
341
reset_statistics()342 void reset_statistics() {
343 m_pos.reset_statistics();
344 m_zero.reset_statistics();
345 value_map::iterator it = m_neg.begin(), end = m_neg.end();
346 for (; it != end; ++it) {
347 it->m_value->reset_statistics();
348 }
349 }
350
display(std::ostream & out) const351 void display(std::ostream& out) const {
352 m_pos.display(out);
353 m_zero.display(out);
354 value_map::iterator it = m_neg.begin(), end = m_neg.end();
355 for (; it != end; ++it) {
356 it->m_value->display(out);
357 }
358 }
359
360 private:
size() const361 unsigned size() const {
362 unsigned sz = m_pos.size();
363 sz += m_zero.size();
364 value_map::iterator it = m_neg.begin(), end = m_neg.end();
365 for (; it != end; ++it) {
366 sz += it->m_value->size();
367 }
368 return sz;
369 }
370 };
371
372 /**
373 \brief priority queue for passive list.
374 */
375
376 class hilbert_basis::passive {
377 struct lt {
378 passive** p;
lthilbert_basis::passive::lt379 lt(passive** p): p(p) {}
operator ()hilbert_basis::passive::lt380 bool operator()(int v1, int v2) const {
381 return (**p)(v1, v2);
382 }
383 };
384 hilbert_basis& hb;
385 svector<offset_t> m_passive;
386 unsigned_vector m_free_list;
387 passive* m_this;
388 lt m_lt;
389 heap<lt> m_heap; // binary heap over weights
390
sum_abs(offset_t idx) const391 numeral sum_abs(offset_t idx) const {
392 numeral w(0);
393 unsigned nv = hb.get_num_vars();
394 for (unsigned i = 0; i < nv; ++i) {
395 w += abs(hb.vec(idx)[i]);
396 }
397 return w;
398 }
399
400 public:
401
passive(hilbert_basis & hb)402 passive(hilbert_basis& hb):
403 hb(hb),
404 m_lt(&m_this),
405 m_heap(10, m_lt)
406 {
407 m_this = this;
408 }
409
reset()410 void reset() {
411 m_heap.reset();
412 m_free_list.reset();
413 m_passive.reset();
414 }
415
empty() const416 bool empty() const {
417 return m_heap.empty();
418 }
419
pop()420 offset_t pop() {
421 SASSERT(!empty());
422 unsigned val = static_cast<unsigned>(m_heap.erase_min());
423 offset_t result = m_passive[val];
424 m_free_list.push_back(val);
425 m_passive[val] = mk_invalid_offset();
426 return result;
427 }
428
insert(offset_t idx)429 void insert(offset_t idx) {
430 unsigned v;
431 if (m_free_list.empty()) {
432 v = m_passive.size();
433 m_passive.push_back(idx);
434 m_heap.set_bounds(v+1);
435 }
436 else {
437 v = m_free_list.back();
438 m_free_list.pop_back();
439 m_passive[v] = idx;
440 }
441 m_heap.insert(v);
442 }
443
444 class iterator {
445 passive& p;
446 unsigned m_idx;
fwd()447 void fwd() {
448 while (m_idx < p.m_passive.size() &&
449 is_invalid_offset(p.m_passive[m_idx])) {
450 ++m_idx;
451 }
452 }
453 public:
iterator(passive & p,unsigned i)454 iterator(passive& p, unsigned i): p(p), m_idx(i) { fwd(); }
operator *() const455 offset_t operator*() const { return p.m_passive[m_idx]; }
operator ++()456 iterator& operator++() { ++m_idx; fwd(); return *this; }
operator ++(int)457 iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
operator ==(iterator const & it) const458 bool operator==(iterator const& it) const {return m_idx == it.m_idx; }
operator !=(iterator const & it) const459 bool operator!=(iterator const& it) const {return m_idx != it.m_idx; }
460 };
461
begin()462 iterator begin() {
463 return iterator(*this, 0);
464 }
465
end()466 iterator end() {
467 return iterator(*this, m_passive.size());
468 }
469
operator ()(int v1,int v2) const470 bool operator()(int v1, int v2) const {
471 offset_t idx1 = m_passive[v1];
472 offset_t idx2 = m_passive[v2];
473 return sum_abs(idx1) < sum_abs(idx2);
474 }
475 };
476
477
478 class hilbert_basis::vector_lt_t {
479 hilbert_basis& hb;
480 public:
vector_lt_t(hilbert_basis & hb)481 vector_lt_t(hilbert_basis& hb): hb(hb) {}
operator ()(offset_t idx1,offset_t idx2) const482 bool operator()(offset_t idx1, offset_t idx2) const {
483 return hb.vector_lt(idx1, idx2);
484 }
485 };
486
487
488 class hilbert_basis::passive2 {
489 struct lt {
490 passive2** p;
lthilbert_basis::passive2::lt491 lt(passive2** p): p(p) {}
operator ()hilbert_basis::passive2::lt492 bool operator()(int v1, int v2) const {
493 return (**p)(v1, v2);
494 }
495 };
496 hilbert_basis& hb;
497 svector<offset_t> m_pos_sos;
498 svector<offset_t> m_neg_sos;
499 vector<numeral> m_pos_sos_sum;
500 vector<numeral> m_neg_sos_sum;
501 vector<numeral> m_sum_abs;
502 unsigned_vector m_psos;
503 svector<offset_t> m_pas;
504 vector<numeral> m_weight;
505 unsigned_vector m_free_list;
506 passive2* m_this;
507 lt m_lt;
508 heap<lt> m_heap; // binary heap over weights
509
sum_abs(offset_t idx) const510 numeral sum_abs(offset_t idx) const {
511 numeral w(0);
512 unsigned nv = hb.get_num_vars();
513 for (unsigned i = 0; i < nv; ++i) {
514 w += abs(hb.vec(idx)[i]);
515 }
516 return w;
517 }
518
519 public:
passive2(hilbert_basis & hb)520 passive2(hilbert_basis& hb):
521 hb(hb),
522 m_lt(&m_this),
523 m_heap(10, m_lt)
524 {
525 m_this = this;
526 }
527
init(svector<offset_t> const & I)528 void init(svector<offset_t> const& I) {
529 for (unsigned i = 0; i < I.size(); ++i) {
530 numeral const& w = hb.vec(I[i]).weight();
531 if (w.is_pos()) {
532 m_pos_sos.push_back(I[i]);
533 m_pos_sos_sum.push_back(sum_abs(I[i]));
534 }
535 else {
536 m_neg_sos.push_back(I[i]);
537 m_neg_sos_sum.push_back(sum_abs(I[i]));
538 }
539 }
540 }
541
reset()542 void reset() {
543 m_heap.reset();
544 m_free_list.reset();
545 m_psos.reset();
546 m_pas.reset();
547 m_sum_abs.reset();
548 m_pos_sos.reset();
549 m_neg_sos.reset();
550 m_pos_sos_sum.reset();
551 m_neg_sos_sum.reset();
552 m_weight.reset();
553 }
554
insert(offset_t idx,unsigned offset)555 void insert(offset_t idx, unsigned offset) {
556 SASSERT(!m_pos_sos.empty());
557 unsigned v;
558 if (m_free_list.empty()) {
559 v = m_pas.size();
560 m_pas.push_back(idx);
561 m_psos.push_back(offset);
562 m_weight.push_back(numeral(0));
563 m_heap.set_bounds(v+1);
564 m_sum_abs.push_back(sum_abs(idx));
565 }
566 else {
567 v = m_free_list.back();
568 m_free_list.pop_back();
569 m_pas[v] = idx;
570 m_psos[v] = offset;
571 m_weight[v] = numeral(0);
572 m_sum_abs[v] = sum_abs(idx);
573 }
574 next_resolvable(hb.vec(idx).weight().is_pos(), v);
575 }
576
empty() const577 bool empty() const {
578 return m_heap.empty();
579 }
580
pop(offset_t & sos,offset_t & pas)581 unsigned pop(offset_t& sos, offset_t& pas) {
582 SASSERT (!empty());
583 unsigned val = static_cast<unsigned>(m_heap.erase_min());
584 pas = m_pas[val];
585 numeral old_weight = hb.vec(pas).weight();
586 bool is_positive = old_weight.is_pos();
587 unsigned psos = m_psos[val];
588 sos = is_positive?m_neg_sos[psos]:m_pos_sos[psos];
589 m_psos[val]++;
590 next_resolvable(is_positive, val);
591 numeral new_weight = hb.vec(sos).weight() + old_weight;
592 if (new_weight.is_pos() != old_weight.is_pos()) {
593 psos = 0;
594 }
595 return psos;
596 }
597
operator ()(int v1,int v2) const598 bool operator()(int v1, int v2) const {
599 return m_weight[v1] < m_weight[v2];
600 }
601
602 class iterator {
603 passive2& p;
604 unsigned m_idx;
fwd()605 void fwd() {
606 while (m_idx < p.m_pas.size() &&
607 is_invalid_offset(p.m_pas[m_idx])) {
608 ++m_idx;
609 }
610 }
611 public:
iterator(passive2 & p,unsigned i)612 iterator(passive2& p, unsigned i): p(p), m_idx(i) { fwd(); }
pas() const613 offset_t pas() const { return p.m_pas[m_idx]; }
sos() const614 offset_t sos() const { return (p.hb.vec(pas()).weight().is_pos()?p.m_neg_sos:p.m_pos_sos)[p.m_psos[m_idx]]; }
operator ++()615 iterator& operator++() { ++m_idx; fwd(); return *this; }
operator ++(int)616 iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
operator ==(iterator const & it) const617 bool operator==(iterator const& it) const {return m_idx == it.m_idx; }
operator !=(iterator const & it) const618 bool operator!=(iterator const& it) const {return m_idx != it.m_idx; }
619 };
620
begin()621 iterator begin() {
622 return iterator(*this, 0);
623 }
624
end()625 iterator end() {
626 return iterator(*this, m_pas.size());
627 }
628 private:
next_resolvable(bool is_positive,unsigned v)629 void next_resolvable(bool is_positive, unsigned v) {
630 offset_t pas = m_pas[v];
631 svector<offset_t> const& soss = is_positive?m_neg_sos:m_pos_sos;
632 while (m_psos[v] < soss.size()) {
633 unsigned psos = m_psos[v];
634 offset_t sos = soss[psos];
635 if (hb.can_resolve(sos, pas, false)) {
636 m_weight[v] = m_sum_abs[v] + (is_positive?m_neg_sos_sum[psos]:m_pos_sos_sum[psos]);
637 m_heap.insert(v);
638 return;
639 }
640 ++m_psos[v];
641 }
642 // add pas to free-list for hb if it is not in sos.
643 m_free_list.push_back(v);
644 m_psos[v] = UINT_MAX;
645 m_pas[v] = mk_invalid_offset();
646 }
647 };
648
649
hilbert_basis(reslimit & lim)650 hilbert_basis::hilbert_basis(reslimit& lim):
651 m_limit(lim),
652 m_use_support(true),
653 m_use_ordered_support(true),
654 m_use_ordered_subsumption(true)
655 {
656 m_index = alloc(index, *this);
657 m_passive = alloc(passive, *this);
658 m_passive2 = alloc(passive2, *this);
659 }
660
~hilbert_basis()661 hilbert_basis::~hilbert_basis() {
662 dealloc(m_index);
663 dealloc(m_passive);
664 dealloc(m_passive2);
665 }
666
mk_invalid_offset()667 hilbert_basis::offset_t hilbert_basis::mk_invalid_offset() {
668 return offset_t(UINT_MAX);
669 }
670
is_invalid_offset(offset_t offs)671 bool hilbert_basis::is_invalid_offset(offset_t offs) {
672 return offs.m_offset == UINT_MAX;
673 }
674
reset()675 void hilbert_basis::reset() {
676 m_ineqs.reset();
677 m_iseq.reset();
678 m_store.reset();
679 m_basis.reset();
680 m_free_list.reset();
681 m_sos.reset();
682 m_zero.reset();
683 m_active.reset();
684 if (m_passive) {
685 m_passive->reset();
686 }
687 if (m_passive2) {
688 m_passive2->reset();
689 }
690 if (m_index) {
691 m_index->reset(1);
692 }
693 m_ints.reset();
694 m_current_ineq = 0;
695
696 }
697
collect_statistics(statistics & st) const698 void hilbert_basis::collect_statistics(statistics& st) const {
699 st.update("hb.num_subsumptions", m_stats.m_num_subsumptions);
700 st.update("hb.num_resolves", m_stats.m_num_resolves);
701 st.update("hb.num_saturations", m_stats.m_num_saturations);
702 st.update("hb.basis_size", get_basis_size());
703 m_index->collect_statistics(st);
704 }
705
reset_statistics()706 void hilbert_basis::reset_statistics() {
707 m_stats.reset();
708 m_index->reset_statistics();
709 }
710
add_ge(rational_vector const & v,rational const & b)711 void hilbert_basis::add_ge(rational_vector const& v, rational const& b) {
712 SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size());
713 num_vector w;
714 w.push_back(to_numeral(-b));
715 for (unsigned i = 0; i < v.size(); ++i) {
716 w.push_back(to_numeral(v[i]));
717 }
718 m_ineqs.push_back(w);
719 m_iseq.push_back(false);
720 }
721
add_le(rational_vector const & v,rational const & b)722 void hilbert_basis::add_le(rational_vector const& v, rational const& b) {
723 rational_vector w(v);
724 for (unsigned i = 0; i < w.size(); ++i) {
725 w[i].neg();
726 }
727 add_ge(w, -b);
728 }
729
add_eq(rational_vector const & v,rational const & b)730 void hilbert_basis::add_eq(rational_vector const& v, rational const& b) {
731 SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size());
732 num_vector w;
733 w.push_back(to_numeral(-b));
734 for (unsigned i = 0; i < v.size(); ++i) {
735 w.push_back(to_numeral(v[i]));
736 }
737 m_ineqs.push_back(w);
738 m_iseq.push_back(true);
739 }
740
add_ge(rational_vector const & v)741 void hilbert_basis::add_ge(rational_vector const& v) {
742 add_ge(v, rational(0));
743 }
744
add_le(rational_vector const & v)745 void hilbert_basis::add_le(rational_vector const& v) {
746 add_le(v, rational(0));
747 }
748
add_eq(rational_vector const & v)749 void hilbert_basis::add_eq(rational_vector const& v) {
750 add_eq(v, rational(0));
751 }
752
set_is_int(unsigned var_index)753 void hilbert_basis::set_is_int(unsigned var_index) {
754 //
755 // The 0'th index is reserved for the constant
756 // coefficient. Shift indices by 1.
757 //
758 m_ints.push_back(var_index+1);
759 }
760
get_is_int(unsigned var_index) const761 bool hilbert_basis::get_is_int(unsigned var_index) const {
762 return m_ints.contains(var_index+1);
763 }
764
get_num_vars() const765 unsigned hilbert_basis::get_num_vars() const {
766 if (m_ineqs.empty()) {
767 return 0;
768 }
769 else {
770 SASSERT(m_ineqs.back().size() > 1);
771 return m_ineqs.back().size();
772 }
773 }
774
vec(offset_t offs) const775 hilbert_basis::values hilbert_basis::vec(offset_t offs) const {
776 return values(m_ineqs.size(), m_store.c_ptr() + offs.m_offset);
777 }
778
init_basis()779 void hilbert_basis::init_basis() {
780 m_basis.reset();
781 m_store.reset();
782 m_free_list.reset();
783 unsigned nv = get_num_vars();
784 for (unsigned i = 0; i < nv; ++i) {
785 add_unit_vector(i, numeral(1));
786 }
787 for (unsigned i = 0; i < m_ints.size(); ++i) {
788 add_unit_vector(m_ints[i], numeral(-1));
789 }
790 }
791
add_unit_vector(unsigned i,numeral const & e)792 void hilbert_basis::add_unit_vector(unsigned i, numeral const& e) {
793 unsigned num_vars = get_num_vars();
794 num_vector w(num_vars, numeral(0));
795 w[i] = e;
796 offset_t idx = alloc_vector();
797 values v = vec(idx);
798 for (unsigned j = 0; j < num_vars; ++j) {
799 v[j] = w[j];
800 }
801 m_basis.push_back(idx);
802 }
803
saturate()804 lbool hilbert_basis::saturate() {
805 init_basis();
806 m_current_ineq = 0;
807 while (checkpoint() && m_current_ineq < m_ineqs.size()) {
808 select_inequality();
809 stopwatch sw;
810 sw.start();
811 lbool r = saturate(m_ineqs[m_current_ineq], m_iseq[m_current_ineq]);
812 IF_VERBOSE(3,
813 { statistics st;
814 collect_statistics(st);
815 st.display(verbose_stream());
816 sw.stop();
817 verbose_stream() << "time: " << sw.get_seconds() << "\n";
818 });
819
820 ++m_stats.m_num_saturations;
821 if (r != l_true) {
822 return r;
823 }
824 ++m_current_ineq;
825 }
826 if (!checkpoint()) {
827 return l_undef;
828 }
829 return l_true;
830 }
831
saturate_orig(num_vector const & ineq,bool is_eq)832 lbool hilbert_basis::saturate_orig(num_vector const& ineq, bool is_eq) {
833 m_active.reset();
834 m_passive->reset();
835 m_zero.reset();
836 m_index->reset(m_current_ineq+1);
837 int_table support;
838 TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq););
839 iterator it = begin();
840 for (; it != end(); ++it) {
841 offset_t idx = *it;
842 values v = vec(idx);
843 v.weight() = get_weight(v, ineq);
844 for (unsigned k = 0; k < m_current_ineq; ++k) {
845 v.weight(k) = get_weight(v, m_ineqs[k]);
846 }
847 add_goal(idx);
848 if (m_use_support) {
849 support.insert(idx.m_offset);
850 }
851 }
852 TRACE("hilbert_basis", display(tout););
853 // resolve passive into active
854 offset_t j = alloc_vector();
855 while (!m_passive->empty()) {
856 if (!checkpoint()) {
857 return l_undef;
858 }
859 offset_t idx = m_passive->pop();
860 TRACE("hilbert_basis", display(tout););
861 if (is_subsumed(idx)) {
862 recycle(idx);
863 continue;
864 }
865 for (unsigned i = 0; checkpoint() && i < m_active.size(); ++i) {
866 if ((!m_use_support || support.contains(m_active[i].m_offset)) && can_resolve(idx, m_active[i], true)) {
867 resolve(idx, m_active[i], j);
868 if (add_goal(j)) {
869 j = alloc_vector();
870 }
871 }
872 }
873 m_active.push_back(idx);
874 }
875 m_free_list.push_back(j);
876 // Move positive from active and zeros to new basis.
877 m_basis.reset();
878 m_basis.append(m_zero);
879 for (unsigned i = 0; !is_eq && i < m_active.size(); ++i) {
880 offset_t idx = m_active[i];
881 if (vec(idx).weight().is_pos()) {
882 m_basis.push_back(idx);
883 }
884 else {
885 m_free_list.push_back(idx);
886 }
887 }
888 m_active.reset();
889 m_passive->reset();
890 m_zero.reset();
891 TRACE("hilbert_basis", display(tout););
892 return m_basis.empty()?l_false:l_true;
893 }
894
vector_lt(offset_t idx1,offset_t idx2) const895 bool hilbert_basis::vector_lt(offset_t idx1, offset_t idx2) const {
896 values v = vec(idx1);
897 values w = vec(idx2);
898 numeral a(0), b(0);
899 for (unsigned i = 0; i < get_num_vars(); ++i) {
900 a += abs(v[i]);
901 b += abs(w[i]);
902 }
903 return a < b;
904 }
905
saturate(num_vector const & ineq,bool is_eq)906 lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) {
907 m_zero.reset();
908 m_index->reset(m_current_ineq+1);
909 m_passive2->reset();
910 m_sos.reset();
911 TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq););
912 unsigned init_basis_size = 0;
913 for (unsigned i = 0; i < m_basis.size(); ++i) {
914 offset_t idx = m_basis[i];
915 values v = vec(idx);
916 v.weight() = get_weight(v, ineq);
917 for (unsigned k = 0; k < m_current_ineq; ++k) {
918 v.weight(k) = get_weight(v, m_ineqs[k]);
919 }
920 m_index->insert(idx, v);
921 if (v.weight().is_zero()) {
922 m_zero.push_back(idx);
923 }
924 else {
925 if (v.weight().is_pos()) {
926 m_basis[init_basis_size++] = idx;
927 }
928 m_sos.push_back(idx);
929 }
930 }
931 m_basis.resize(init_basis_size);
932 m_passive2->init(m_sos);
933 // ASSERT basis is sorted by weight.
934
935 // initialize passive
936 for (unsigned i = 0; (init_basis_size > 0) && i < m_sos.size(); ++i) {
937 if (vec(m_sos[i]).weight().is_neg()) {
938 m_passive2->insert(m_sos[i], 0);
939 }
940 }
941
942 TRACE("hilbert_basis", display(tout););
943 // resolve passive into active
944 offset_t idx = alloc_vector();
945 while (checkpoint() && !m_passive2->empty()) {
946 offset_t sos, pas;
947 TRACE("hilbert_basis", display(tout); );
948 unsigned offset = m_passive2->pop(sos, pas);
949 SASSERT(can_resolve(sos, pas, true));
950 resolve(sos, pas, idx);
951 if (is_subsumed(idx)) {
952 continue;
953 }
954 values v = vec(idx);
955 m_index->insert(idx, v);
956 if (v.weight().is_zero()) {
957 m_zero.push_back(idx);
958 }
959 else {
960 if (!m_use_ordered_support) {
961 offset = 0;
962 }
963 m_passive2->insert(idx, offset);
964 if (v.weight().is_pos()) {
965 m_basis.push_back(idx);
966 }
967 }
968 idx = alloc_vector();
969 }
970 if (!checkpoint()) {
971 return l_undef;
972 }
973
974 m_free_list.push_back(idx);
975 // remove positive values from basis if we are looking for an equality.
976 while (is_eq && !m_basis.empty()) {
977 m_free_list.push_back(m_basis.back());
978 m_basis.pop_back();
979 }
980 m_basis.append(m_zero);
981 std::sort(m_basis.begin(), m_basis.end(), vector_lt_t(*this));
982 m_zero.reset();
983 TRACE("hilbert_basis", display(tout););
984 return m_basis.empty()?l_false:l_true;
985 }
986
get_basis_solution(unsigned i,rational_vector & v,bool & is_initial)987 void hilbert_basis::get_basis_solution(unsigned i, rational_vector& v, bool& is_initial) {
988 offset_t offs = m_basis[i];
989 v.reset();
990 for (unsigned i = 1; i < get_num_vars(); ++i) {
991 v.push_back(to_rational(vec(offs)[i]));
992 }
993 is_initial = !vec(offs)[0].is_zero();
994 }
995
get_ge(unsigned i,rational_vector & v,rational & b,bool & is_eq)996 void hilbert_basis::get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq) {
997 v.reset();
998 for (unsigned j = 1; j < m_ineqs[i].size(); ++j) {
999 v.push_back(to_rational(m_ineqs[i][j]));
1000 }
1001 b = to_rational(-m_ineqs[i][0]);
1002 is_eq = m_iseq[i];
1003 }
1004
1005
select_inequality()1006 void hilbert_basis::select_inequality() {
1007 SASSERT(m_current_ineq < m_ineqs.size());
1008 unsigned best = m_current_ineq;
1009 unsigned non_zeros = get_num_nonzeros(m_ineqs[best]);
1010 unsigned prod = get_ineq_product(m_ineqs[best]);
1011 for (unsigned j = best+1; prod != 0 && j < m_ineqs.size(); ++j) {
1012 unsigned non_zeros2 = get_num_nonzeros(m_ineqs[j]);
1013 unsigned prod2 = get_ineq_product(m_ineqs[j]);
1014 if (prod2 == 0) {
1015 prod = prod2;
1016 non_zeros = non_zeros2;
1017 best = j;
1018 break;
1019 }
1020 if (non_zeros2 < non_zeros || (non_zeros2 == non_zeros && prod2 < prod)) {
1021 prod = prod2;
1022 non_zeros = non_zeros2;
1023 best = j;
1024 }
1025 }
1026 if (best != m_current_ineq) {
1027 std::swap(m_ineqs[m_current_ineq], m_ineqs[best]);
1028 std::swap(m_iseq[m_current_ineq], m_iseq[best]);
1029 }
1030 }
1031
get_num_nonzeros(num_vector const & ineq)1032 unsigned hilbert_basis::get_num_nonzeros(num_vector const& ineq) {
1033 unsigned count = 0;
1034 for (unsigned i = 0; i < ineq.size(); ++i) {
1035 if (!ineq[i].is_zero()) {
1036 ++count;
1037 }
1038 }
1039 return count;
1040 }
1041
get_ineq_product(num_vector const & ineq)1042 unsigned hilbert_basis::get_ineq_product(num_vector const& ineq) {
1043 unsigned num_pos = 0, num_neg = 0;
1044 iterator it = begin();
1045 for (; it != end(); ++it) {
1046 values v = vec(*it);
1047 numeral w = get_weight(v, ineq);
1048 if (w.is_pos()) {
1049 ++num_pos;
1050 }
1051 else if (w.is_neg()) {
1052 ++num_neg;
1053 }
1054 }
1055 return num_pos * num_neg;
1056 }
1057
get_ineq_diff(num_vector const & ineq)1058 hilbert_basis::numeral hilbert_basis::get_ineq_diff(num_vector const& ineq) {
1059 numeral max_pos(0), min_neg(0);
1060 iterator it = begin();
1061 for (; it != end(); ++it) {
1062 values v = vec(*it);
1063 numeral w = get_weight(v, ineq);
1064 if (w > max_pos) {
1065 max_pos = w;
1066 }
1067 else if (w < min_neg) {
1068 min_neg = w;
1069 }
1070 }
1071 return max_pos - min_neg;
1072 }
1073
recycle(offset_t idx)1074 void hilbert_basis::recycle(offset_t idx) {
1075 m_index->remove(idx, vec(idx));
1076 m_free_list.push_back(idx);
1077 }
1078
resolve(offset_t i,offset_t j,offset_t r)1079 void hilbert_basis::resolve(offset_t i, offset_t j, offset_t r) {
1080 ++m_stats.m_num_resolves;
1081 values v = vec(i);
1082 values w = vec(j);
1083 values u = vec(r);
1084 unsigned nv = get_num_vars();
1085 for (unsigned k = 0; k < nv; ++k) {
1086 u[k] = v[k] + w[k];
1087 }
1088 u.weight() = v.weight() + w.weight();
1089 for (unsigned k = 0; k < m_current_ineq; ++k) {
1090 u.weight(k) = v.weight(k) + w.weight(k);
1091 }
1092 TRACE("hilbert_basis_verbose",
1093 display(tout, i);
1094 display(tout, j);
1095 display(tout, r);
1096 );
1097 }
1098
1099
alloc_vector()1100 hilbert_basis::offset_t hilbert_basis::alloc_vector() {
1101 if (m_free_list.empty()) {
1102 unsigned sz = m_ineqs.size() + get_num_vars();
1103 unsigned idx = m_store.size();
1104 m_store.resize(idx + sz);
1105 // std::cout << "alloc vector: " << idx << " " << sz << " " << m_store.c_ptr() + idx << " " << m_ineqs.size() << "\n";
1106 return offset_t(idx);
1107 }
1108 else {
1109 offset_t result = m_free_list.back();
1110 m_free_list.pop_back();
1111 return result;
1112 }
1113 }
1114
checkpoint()1115 bool hilbert_basis::checkpoint() {
1116 return m_limit.inc();
1117 }
1118
add_goal(offset_t idx)1119 bool hilbert_basis::add_goal(offset_t idx) {
1120 TRACE("hilbert_basis", display(tout, idx););
1121 values v = vec(idx);
1122 if (is_subsumed(idx)) {
1123 return false;
1124 }
1125 m_index->insert(idx, v);
1126 if (v.weight().is_zero()) {
1127 m_zero.push_back(idx);
1128 }
1129 else {
1130 m_passive->insert(idx);
1131 }
1132 return true;
1133 }
1134
is_subsumed(offset_t idx)1135 bool hilbert_basis::is_subsumed(offset_t idx) {
1136
1137 if (m_index->find(idx, vec(idx))) {
1138 ++m_stats.m_num_subsumptions;
1139 return true;
1140 }
1141 return false;
1142 }
1143
can_resolve(offset_t i,offset_t j,bool check_sign) const1144 bool hilbert_basis::can_resolve(offset_t i, offset_t j, bool check_sign) const {
1145 if (check_sign && get_sign(i) == get_sign(j)) {
1146 return false;
1147 }
1148 SASSERT(get_sign(i) != get_sign(j));
1149 values const& v1 = vec(i);
1150 values const& v2 = vec(j);
1151 if (v1[0].is_one() && v2[0].is_one()) {
1152 return false;
1153 }
1154 for (unsigned i = 0; i < m_ints.size(); ++i) {
1155 unsigned j = m_ints[i];
1156 if (v1[j].is_pos() && v2[j].is_neg()) {
1157 return false;
1158 }
1159 if (v1[j].is_neg() && v2[j].is_pos()) {
1160 return false;
1161 }
1162 }
1163 return true;
1164 }
1165
get_sign(offset_t idx) const1166 hilbert_basis::sign_t hilbert_basis::get_sign(offset_t idx) const {
1167 numeral const& val = vec(idx).weight();
1168 if (val.is_pos()) {
1169 return pos;
1170 }
1171 if (val.is_neg()) {
1172 return neg;
1173 }
1174 return zero;
1175 }
1176
get_weight(values const & val,num_vector const & ineq) const1177 hilbert_basis::numeral hilbert_basis::get_weight(values const & val, num_vector const& ineq) const {
1178 numeral result(0);
1179 unsigned num_vars = get_num_vars();
1180 for (unsigned i = 0; i < num_vars; ++i) {
1181 result += val[i]*ineq[i];
1182 }
1183 return result;
1184 }
1185
display(std::ostream & out) const1186 void hilbert_basis::display(std::ostream& out) const {
1187 out << "inequalities:\n";
1188 for (unsigned i = 0; i < m_ineqs.size(); ++i) {
1189 display_ineq(out, m_ineqs[i], m_iseq[i]);
1190 }
1191 if (!m_basis.empty()) {
1192 out << "basis:\n";
1193 for (iterator it = begin(); it != end(); ++it) {
1194 display(out, *it);
1195 }
1196 }
1197 if (!m_active.empty()) {
1198 out << "active:\n";
1199 for (unsigned i = 0; i < m_active.size(); ++i) {
1200 display(out, m_active[i]);
1201 }
1202 }
1203 if (!m_passive->empty()) {
1204 passive::iterator it = m_passive->begin();
1205 passive::iterator end = m_passive->end();
1206 out << "passive:\n";
1207 for (; it != end; ++it) {
1208 display(out, *it);
1209 }
1210 }
1211 if (!m_passive2->empty()) {
1212 passive2::iterator it = m_passive2->begin();
1213 passive2::iterator end = m_passive2->end();
1214 out << "passive:\n";
1215 for (; it != end; ++it) {
1216 display(out << "sos:", it.sos());
1217 display(out << "pas:", it.pas());
1218 }
1219 }
1220 if (!m_zero.empty()) {
1221 out << "zero:\n";
1222 for (unsigned i = 0; i < m_zero.size(); ++i) {
1223 display(out, m_zero[i]);
1224 }
1225 }
1226 if (m_index) {
1227 m_index->display(out);
1228 }
1229 }
1230
display(std::ostream & out,offset_t o) const1231 void hilbert_basis::display(std::ostream& out, offset_t o) const {
1232 display(out, vec(o));
1233 out << " -> " << vec(o).weight() << "\n";
1234 }
1235
display(std::ostream & out,values const & v) const1236 void hilbert_basis::display(std::ostream& out, values const& v) const {
1237 unsigned nv = get_num_vars();
1238 for (unsigned j = 0; j < nv; ++j) {
1239 out << v[j] << " ";
1240 }
1241 }
1242
display_ineq(std::ostream & out,num_vector const & v,bool is_eq) const1243 void hilbert_basis::display_ineq(std::ostream& out, num_vector const& v, bool is_eq) const {
1244 unsigned nv = v.size();
1245 for (unsigned j = 1; j < nv; ++j) {
1246 if (!v[j].is_zero()) {
1247 if (j > 0) {
1248 if (v[j].is_pos()) {
1249 out << " + ";
1250 }
1251 else {
1252 out << " - ";
1253 }
1254 }
1255 else if (j == 0 && v[0].is_neg()) {
1256 out << "-";
1257 }
1258 if (!v[j].is_one() && !v[j].is_minus_one()) {
1259 out << abs(v[j]) << "*";
1260 }
1261 out << "x" << j;
1262 }
1263 }
1264 if (is_eq) {
1265 out << " = " << -v[0] << "\n";
1266 }
1267 else {
1268 out << " >= " << -v[0] << "\n";
1269 }
1270 }
1271
1272
1273 /**
1274 Vector v is subsumed by vector w if
1275
1276 v[i] >= w[i] for each index i.
1277
1278 a*v >= a*w for the evaluation of vectors with respect to a.
1279
1280 . a*v < 0 => a*v = a*w
1281 . a*v > 0 => a*w > 0
1282 . a*v = 0 => a*w = 0
1283
1284 Justification:
1285
1286 let u := v - w, then
1287
1288 u[i] >= 0 for each index i
1289
1290 a*u = a*(v-w) >= 0
1291
1292 So v = u + w, where a*u >= 0, a*w >= 0.
1293
1294 If a*v >= a*w >= 0 then v and w are linear
1295 solutions of e_i, and also v-w is a solution.
1296
1297 If a*v = a*w < 0, then a*(v-w) = 0, so v can be obtained from w + (v - w).
1298
1299 */
1300
is_subsumed(offset_t i,offset_t j) const1301 bool hilbert_basis::is_subsumed(offset_t i, offset_t j) const {
1302 values v = vec(i);
1303 values w = vec(j);
1304 numeral const& n = v.weight();
1305 numeral const& m = w.weight();
1306 bool r =
1307 i.m_offset != j.m_offset &&
1308 n >= m && (!m.is_neg() || n == m) &&
1309 is_geq(v, w);
1310 for (unsigned k = 0; r && k < m_current_ineq; ++k) {
1311 r = v.weight(k) >= w.weight(k);
1312 }
1313 CTRACE("hilbert_basis", r,
1314 display(tout, i);
1315 tout << " <= \n";
1316 display(tout, j);
1317 tout << "\n";);
1318 return r;
1319 }
1320
is_geq(values const & v,values const & w) const1321 bool hilbert_basis::is_geq(values const& v, values const& w) const {
1322 unsigned nv = get_num_vars();
1323 for (unsigned i = 0; i < nv; ++i) {
1324 if (!is_abs_geq(v[i], w[i])) {
1325 return false;
1326 }
1327 }
1328 return true;
1329 }
1330
is_abs_geq(numeral const & v,numeral const & w) const1331 bool hilbert_basis::is_abs_geq(numeral const& v, numeral const& w) const {
1332 if (w.is_neg()) {
1333 return v <= w;
1334 }
1335 else {
1336 return v >= w;
1337 }
1338 }
1339