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