3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 01:25:31 +00:00

re-organization of muz

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2013-08-28 22:11:33 -07:00
parent 9e61820125
commit e4338f085b
37 changed files with 6 additions and 875 deletions

View file

@ -1 +1,2 @@
muZ and Quantifier Elimination modules
muZ: routines related to solving satisfiability of Horn clauses and
solving Datalog programs.

View file

@ -1,35 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
equiv_proof_converter.cpp
Abstract:
Proof converter that applies equivalence rule to leaves.
Author:
Nikolaj Bjorner (nbjorner) 2012-11-23
Revision History:
--*/
#include "equiv_proof_converter.h"
#include "ast_pp.h"
#include "scoped_proof.h"
void equiv_proof_converter::insert(expr* fml1, expr* fml2) {
if (fml1 != fml2) {
scoped_proof _sp(m);
proof_ref p1(m), p2(m), p3(m);
p1 = m.mk_asserted(fml1);
p2 = m.mk_rewrite(fml1, fml2);
p3 = m.mk_modus_ponens(p1, p2);
TRACE("proof_converter", tout << mk_pp(p3.get(), m) << "\n";);
SASSERT(m.has_fact(p3));
m_replace.insert(p3);
}
}

View file

@ -1,52 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
equiv_proof_converter.h
Abstract:
Proof converter that applies equivalence rule to leaves.
Given a proof P with occurrences of [asserted fml]
replace [asserted fml] by a proof of the form
[mp [asserted fml'] [~ fml fml']]
Author:
Nikolaj Bjorner (nbjorner) 2012-11-23
Revision History:
--*/
#ifndef _EQUIV_PROOF_CONVERTER_H_
#define _EQUIV_PROOF_CONVERTER_H_
#include "replace_proof_converter.h"
class equiv_proof_converter : public proof_converter {
ast_manager& m;
replace_proof_converter m_replace;
public:
equiv_proof_converter(ast_manager& m): m(m), m_replace(m) {}
virtual ~equiv_proof_converter() {}
virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) {
m_replace(m, num_source, source, result);
}
virtual proof_converter * translate(ast_translation & translator) {
return m_replace.translate(translator);
}
void insert(expr* fml1, expr* fml2);
ast_manager& get_manager() { return m; }
};
#endif

View file

@ -1,666 +0,0 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
heap_trie.h
Abstract:
Heap trie structure.
Structure that lets you retrieve point-wise smaller entries
of a tuple. A lookup is to identify entries whose keys
are point-wise dominated by the lookup key.
Author:
Nikolaj Bjorner (nbjorner) 2013-02-15.
Notes:
tries are unordered vectors of keys. This could be enhanced to use either
heaps or sorting. The problem with using the heap implementation directly is that there is no way to
retrieve elements less or equal to a key that is not already in the heap.
If nodes have only a few elements, then this would also be a bloated data-structure to maintain.
Nodes are not de-allocated. Their reference count indicates if they are valid.
Possibly, add garbage collection.
Maintaining sorted ranges for larger domains is another option.
Another possible enhancement is to resplay the tree.
Keep current key index in the nodes.
--*/
#ifndef _HEAP_TRIE_H_
#define _HEAP_TRIE_H_
#include "map.h"
#include "vector.h"
#include "buffer.h"
#include "statistics.h"
#include "small_object_allocator.h"
template<typename Key, typename KeyLE, typename KeyHash, typename Value>
class heap_trie {
struct stats {
unsigned m_num_inserts;
unsigned m_num_removes;
unsigned m_num_find_eq;
unsigned m_num_find_le;
unsigned m_num_find_le_nodes;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
enum node_t {
trie_t,
leaf_t
};
class node {
node_t m_type;
unsigned m_ref;
public:
node(node_t t): m_type(t), m_ref(0) {}
virtual ~node() {}
node_t type() const { return m_type; }
void inc_ref() { ++m_ref; }
void dec_ref() { SASSERT(m_ref > 0); --m_ref; }
unsigned ref_count() const { return m_ref; }
virtual void display(std::ostream& out, unsigned indent) const = 0;
virtual unsigned num_nodes() const = 0;
virtual unsigned num_leaves() const = 0;
};
class leaf : public node {
Value m_value;
public:
leaf(): node(leaf_t) {}
virtual ~leaf() {}
Value const& get_value() const { return m_value; }
void set_value(Value const& v) { m_value = v; }
virtual void display(std::ostream& out, unsigned indent) const {
out << " value: " << m_value;
}
virtual unsigned num_nodes() const { return 1; }
virtual unsigned num_leaves() const { return this->ref_count()>0?1:0; }
};
typedef buffer<std::pair<Key,node*>, true, 2> children_t;
// lean trie node
class trie : public node {
children_t m_nodes;
public:
trie(): node(trie_t) {}
virtual ~trie() {
}
node* find_or_insert(Key k, node* n) {
for (unsigned i = 0; i < m_nodes.size(); ++i) {
if (m_nodes[i].first == k) {
return m_nodes[i].second;
}
}
m_nodes.push_back(std::make_pair(k, n));
return n;
}
bool find(Key k, node*& n) const {
for (unsigned i = 0; i < m_nodes.size(); ++i) {
if (m_nodes[i].first == k) {
n = m_nodes[i].second;
return n->ref_count() > 0;
}
}
return false;
}
// push nodes whose keys are <= key into vector.
void find_le(KeyLE& le, Key key, ptr_vector<node>& nodes) {
for (unsigned i = 0; i < m_nodes.size(); ++i) {
if (le.le(m_nodes[i].first, key)) {
node* n = m_nodes[i].second;
if (n->ref_count() > 0){
nodes.push_back(n);
}
}
}
}
children_t const& nodes() const { return m_nodes; }
children_t & nodes() { return m_nodes; }
virtual void display(std::ostream& out, unsigned indent) const {
for (unsigned j = 0; j < m_nodes.size(); ++j) {
if (j != 0 || indent > 0) {
out << "\n";
}
for (unsigned i = 0; i < indent; ++i) {
out << " ";
}
node* n = m_nodes[j].second;
out << m_nodes[j].first << " refs: " << n->ref_count();
n->display(out, indent + 1);
}
}
virtual unsigned num_nodes() const {
unsigned sz = 1;
for (unsigned j = 0; j < m_nodes.size(); ++j) {
sz += m_nodes[j].second->num_nodes();
}
return sz;
}
virtual unsigned num_leaves() const {
unsigned sz = 0;
for (unsigned j = 0; j < m_nodes.size(); ++j) {
sz += m_nodes[j].second->num_leaves();
}
return sz;
}
private:
bool contains(Key k) {
for (unsigned j = 0; j < m_nodes.size(); ++j) {
if (m_nodes[j].first == k) {
return true;
}
}
return false;
}
};
small_object_allocator m_alloc;
KeyLE& m_le;
unsigned m_num_keys;
unsigned_vector m_keys;
unsigned m_do_reshuffle;
node* m_root;
stats m_stats;
node* m_spare_leaf;
node* m_spare_trie;
public:
heap_trie(KeyLE& le):
m_alloc("heap_trie"),
m_le(le),
m_num_keys(0),
m_do_reshuffle(4),
m_root(0),
m_spare_leaf(0),
m_spare_trie(0)
{}
~heap_trie() {
del_node(m_root);
del_node(m_spare_leaf);
del_node(m_spare_trie);
}
unsigned size() const {
return m_root?m_root->num_leaves():0;
}
void reset(unsigned num_keys) {
del_node(m_root);
del_node(m_spare_leaf);
del_node(m_spare_trie);
m_num_keys = num_keys;
m_keys.resize(num_keys);
for (unsigned i = 0; i < num_keys; ++i) {
m_keys[i] = i;
}
m_root = mk_trie();
m_spare_trie = mk_trie();
m_spare_leaf = mk_leaf();
}
void insert(Key const* keys, Value const& val) {
++m_stats.m_num_inserts;
insert(m_root, num_keys(), keys, m_keys.c_ptr(), val);
#if 0
if (m_stats.m_num_inserts == (1 << m_do_reshuffle)) {
m_do_reshuffle++;
reorder_keys();
}
#endif
}
bool find_eq(Key const* keys, Value& value) {
++m_stats.m_num_find_eq;
node* n = m_root;
node* m;
for (unsigned i = 0; i < num_keys(); ++i) {
if (!to_trie(n)->find(get_key(keys, i), m)) {
return false;
}
n = m;
}
value = to_leaf(n)->get_value();
return true;
}
void find_all_le(Key const* keys, vector<Value>& values) {
++m_stats.m_num_find_le;
ptr_vector<node> todo[2];
todo[0].push_back(m_root);
bool index = false;
for (unsigned i = 0; i < num_keys(); ++i) {
for (unsigned j = 0; j < todo[index].size(); ++j) {
++m_stats.m_num_find_le_nodes;
to_trie(todo[index][j])->find_le(m_le, get_key(keys, i), todo[!index]);
}
todo[index].reset();
index = !index;
}
for (unsigned j = 0; j < todo[index].size(); ++j) {
values.push_back(to_leaf(todo[index][j])->get_value());
}
}
// callback based find function
class check_value {
public:
virtual bool operator()(Value const& v) = 0;
};
bool find_le(Key const* keys, check_value& check) {
++m_stats.m_num_find_le;
++m_stats.m_num_find_le_nodes;
return find_le(m_root, 0, keys, check);
}
void remove(Key const* keys) {
++m_stats.m_num_removes;
// assumption: key is in table.
node* n = m_root;
node* m;
for (unsigned i = 0; i < num_keys(); ++i) {
n->dec_ref();
VERIFY (to_trie(n)->find(get_key(keys, i), m));
n = m;
}
n->dec_ref();
}
void reset_statistics() {
m_stats.reset();
}
void collect_statistics(statistics& st) const {
st.update("heap_trie.num_inserts", m_stats.m_num_inserts);
st.update("heap_trie.num_removes", m_stats.m_num_removes);
st.update("heap_trie.num_find_eq", m_stats.m_num_find_eq);
st.update("heap_trie.num_find_le", m_stats.m_num_find_le);
st.update("heap_trie.num_find_le_nodes", m_stats.m_num_find_le_nodes);
if (m_root) st.update("heap_trie.num_nodes", m_root->num_nodes());
unsigned_vector nums;
ptr_vector<node> todo;
if (m_root) todo.push_back(m_root);
while (!todo.empty()) {
node* n = todo.back();
todo.pop_back();
if (is_trie(n)) {
trie* t = to_trie(n);
unsigned sz = t->nodes().size();
if (nums.size() <= sz) {
nums.resize(sz+1);
}
++nums[sz];
for (unsigned i = 0; i < sz; ++i) {
todo.push_back(t->nodes()[i].second);
}
}
}
if (nums.size() < 16) nums.resize(16);
st.update("heap_trie.num_1_children", nums[1]);
st.update("heap_trie.num_2_children", nums[2]);
st.update("heap_trie.num_3_children", nums[3]);
st.update("heap_trie.num_4_children", nums[4]);
st.update("heap_trie.num_5_children", nums[5]);
st.update("heap_trie.num_6_children", nums[6]);
st.update("heap_trie.num_7_children", nums[7]);
st.update("heap_trie.num_8_children", nums[8]);
st.update("heap_trie.num_9_children", nums[9]);
st.update("heap_trie.num_10_children", nums[10]);
st.update("heap_trie.num_11_children", nums[11]);
st.update("heap_trie.num_12_children", nums[12]);
st.update("heap_trie.num_13_children", nums[13]);
st.update("heap_trie.num_14_children", nums[14]);
st.update("heap_trie.num_15_children", nums[15]);
unsigned sz = 0;
for (unsigned i = 16; i < nums.size(); ++i) {
sz += nums[i];
}
st.update("heap_trie.num_16+_children", sz);
}
void display(std::ostream& out) const {
m_root->display(out, 0);
out << "\n";
}
class iterator {
ptr_vector<node> m_path;
unsigned_vector m_idx;
vector<Key> m_keys;
unsigned m_count;
public:
iterator(node* n) {
if (!n) {
m_count = UINT_MAX;
}
else {
m_count = 0;
first(n);
}
}
Key const* keys() {
return m_keys.c_ptr();
}
Value const& value() const {
return to_leaf(m_path.back())->get_value();
}
iterator& operator++() { fwd(); return *this; }
iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
bool operator==(iterator const& it) const {return m_count == it.m_count; }
bool operator!=(iterator const& it) const {return m_count != it.m_count; }
private:
void first(node* r) {
SASSERT(r->ref_count() > 0);
while (is_trie(r)) {
trie* t = to_trie(r);
m_path.push_back(r);
unsigned sz = t->nodes().size();
for (unsigned i = 0; i < sz; ++i) {
r = t->nodes()[i].second;
if (r->ref_count() > 0) {
m_idx.push_back(i);
m_keys.push_back(t->nodes()[i].first);
break;
}
}
}
SASSERT(is_leaf(r));
m_path.push_back(r);
}
void fwd() {
if (m_path.empty()) {
m_count = UINT_MAX;
return;
}
m_path.pop_back();
while (!m_path.empty()) {
trie* t = to_trie(m_path.back());
unsigned idx = m_idx.back();
unsigned sz = t->nodes().size();
m_idx.pop_back();
m_keys.pop_back();
for (unsigned i = idx+1; i < sz; ++i) {
node* r = t->nodes()[i].second;
if (r->ref_count() > 0) {
m_idx.push_back(i);
m_keys.push_back(t->nodes()[i].first);
first(r);
++m_count;
return;
}
}
m_path.pop_back();
}
m_count = UINT_MAX;
}
};
iterator begin() const {
return iterator(m_root);
}
iterator end() const {
return iterator(0);
}
private:
inline unsigned num_keys() const {
return m_num_keys;
}
inline Key const& get_key(Key const* keys, unsigned i) const {
return keys[m_keys[i]];
}
struct KeyEq {
bool operator()(Key const& k1, Key const& k2) const {
return k1 == k2;
}
};
typedef hashtable<Key, KeyHash, KeyEq> key_set;
struct key_info {
unsigned m_index;
unsigned m_index_size;
key_info(unsigned i, unsigned sz):
m_index(i),
m_index_size(sz)
{}
bool operator<(key_info const& other) const {
return
(m_index_size < other.m_index_size) ||
((m_index_size == other.m_index_size) &&
(m_index < other.m_index));
}
};
void reorder_keys() {
vector<key_set> weights;
weights.resize(num_keys());
unsigned_vector depth;
ptr_vector<node> nodes;
depth.push_back(0);
nodes.push_back(m_root);
while (!nodes.empty()) {
node* n = nodes.back();
unsigned d = depth.back();
nodes.pop_back();
depth.pop_back();
if (is_trie(n)) {
trie* t = to_trie(n);
unsigned sz = t->nodes().size();
for (unsigned i = 0; i < sz; ++i) {
nodes.push_back(t->nodes()[i].second);
depth.push_back(d+1);
weights[d].insert(t->nodes()[i].first);
}
}
}
SASSERT(weights.size() == num_keys());
svector<key_info> infos;
unsigned sz = 0;
bool is_sorted = true;
for (unsigned i = 0; i < weights.size(); ++i) {
unsigned sz2 = weights[i].size();
if (sz > sz2) {
is_sorted = false;
}
sz = sz2;
infos.push_back(key_info(i, sz));
}
if (is_sorted) {
return;
}
std::sort(infos.begin(), infos.end());
unsigned_vector sorted_keys, new_keys;
for (unsigned i = 0; i < num_keys(); ++i) {
unsigned j = infos[i].m_index;
sorted_keys.push_back(j);
new_keys.push_back(m_keys[j]);
}
// m_keys: i |-> key_index
// new_keys: i |-> new_key_index
// permutation: key_index |-> new_key_index
SASSERT(sorted_keys.size() == num_keys());
SASSERT(new_keys.size() == num_keys());
SASSERT(m_keys.size() == num_keys());
iterator it = begin();
trie* new_root = mk_trie();
IF_VERBOSE(2, verbose_stream() << "before reshuffle: " << m_root->num_nodes() << " nodes\n";);
for (; it != end(); ++it) {
IF_VERBOSE(2,
for (unsigned i = 0; i < num_keys(); ++i) {
for (unsigned j = 0; j < num_keys(); ++j) {
if (m_keys[j] == i) {
verbose_stream() << it.keys()[j] << " ";
break;
}
}
}
verbose_stream() << " |-> " << it.value() << "\n";);
insert(new_root, num_keys(), it.keys(), sorted_keys.c_ptr(), it.value());
}
del_node(m_root);
m_root = new_root;
for (unsigned i = 0; i < m_keys.size(); ++i) {
m_keys[i] = new_keys[i];
}
IF_VERBOSE(2, verbose_stream() << "after reshuffle: " << new_root->num_nodes() << " nodes\n";);
IF_VERBOSE(2,
it = begin();
for (; it != end(); ++it) {
for (unsigned i = 0; i < num_keys(); ++i) {
for (unsigned j = 0; j < num_keys(); ++j) {
if (m_keys[j] == i) {
verbose_stream() << it.keys()[j] << " ";
break;
}
}
}
verbose_stream() << " |-> " << it.value() << "\n";
});
}
bool find_le(node* n, unsigned index, Key const* keys, check_value& check) {
if (index == num_keys()) {
SASSERT(n->ref_count() > 0);
bool r = check(to_leaf(n)->get_value());
IF_VERBOSE(2,
for (unsigned j = 0; j < index; ++j) {
verbose_stream() << " ";
}
verbose_stream() << to_leaf(n)->get_value() << (r?" hit\n":" miss\n"););
return r;
}
else {
Key const& key = get_key(keys, index);
children_t& nodes = to_trie(n)->nodes();
for (unsigned i = 0; i < nodes.size(); ++i) {
++m_stats.m_num_find_le_nodes;
node* m = nodes[i].second;
IF_VERBOSE(2,
for (unsigned j = 0; j < index; ++j) {
verbose_stream() << " ";
}
verbose_stream() << nodes[i].first << " <=? " << key << " rc:" << m->ref_count() << "\n";);
if (m->ref_count() > 0 && m_le.le(nodes[i].first, key) && find_le(m, index+1, keys, check)) {
if (i > 0) {
std::swap(nodes[i], nodes[0]);
}
return true;
}
}
return false;
}
}
void insert(node* n, unsigned num_keys, Key const* keys, unsigned const* permutation, Value const& val) {
// assumption: key is not in table.
for (unsigned i = 0; i < num_keys; ++i) {
n->inc_ref();
n = insert_key(to_trie(n), (i + 1 == num_keys), keys[permutation[i]]);
}
n->inc_ref();
to_leaf(n)->set_value(val);
SASSERT(n->ref_count() == 1);
}
node* insert_key(trie* n, bool is_leaf, Key const& key) {
node* m1 = is_leaf?m_spare_leaf:m_spare_trie;
node* m2 = n->find_or_insert(key, m1);
if (m1 == m2) {
if (is_leaf) {
m_spare_leaf = mk_leaf();
}
else {
m_spare_trie = mk_trie();
}
}
return m2;
}
leaf* mk_leaf() {
void* mem = m_alloc.allocate(sizeof(leaf));
return new (mem) leaf();
}
trie* mk_trie() {
void* mem = m_alloc.allocate(sizeof(trie));
return new (mem) trie();
}
void del_node(node* n) {
if (!n) {
return;
}
if (is_trie(n)) {
trie* t = to_trie(n);
for (unsigned i = 0; i < t->nodes().size(); ++i) {
del_node(t->nodes()[i].second);
}
t->~trie();
m_alloc.deallocate(sizeof(trie), t);
}
else {
leaf* l = to_leaf(n);
l->~leaf();
m_alloc.deallocate(sizeof(leaf), l);
}
}
static trie* to_trie(node* n) {
SASSERT(is_trie(n));
return static_cast<trie*>(n);
}
static leaf* to_leaf(node* n) {
SASSERT(is_leaf(n));
return static_cast<leaf*>(n);
}
static bool is_leaf(node* n) {
return n->type() == leaf_t;
}
static bool is_trie(node* n) {
return n->type() == trie_t;
}
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,201 +0,0 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
hilbert_basis.h
Abstract:
Basic Hilbert Basis computation.
hilbert_basis computes a Hilbert basis for linear
homogeneous inequalities over naturals.
Author:
Nikolaj Bjorner (nbjorner) 2013-02-09.
Revision History:
Hilbert basis can be templatized
based on traits that define numeral:
as rational, mpz, checked_int64
(checked or unchecked).
--*/
#ifndef _HILBERT_BASIS_H_
#define _HILBERT_BASIS_H_
#include "rational.h"
#include "lbool.h"
#include "statistics.h"
#include "checked_int64.h"
typedef vector<rational> rational_vector;
class hilbert_basis {
static const bool check = true;
typedef checked_int64<check> numeral;
typedef vector<numeral> num_vector;
static checked_int64<check> to_numeral(rational const& r) {
if (!r.is_int64()) {
throw checked_int64<check>::overflow_exception();
}
return checked_int64<check>(r.get_int64());
}
static rational to_rational(checked_int64<check> const& i) {
return rational(i.get_int64(), rational::i64());
}
class value_index1;
class value_index2;
class value_index3;
class index;
class passive;
class passive2;
struct offset_t {
unsigned m_offset;
offset_t(unsigned o) : m_offset(o) {}
offset_t(): m_offset(0) {}
bool operator<(offset_t const& other) const {
return m_offset < other.m_offset;
}
};
enum sign_t { pos, neg, zero };
struct stats {
unsigned m_num_subsumptions;
unsigned m_num_resolves;
unsigned m_num_saturations;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
class values {
numeral* m_values;
public:
values(unsigned offset, numeral* v): m_values(v+offset) { }
numeral& weight() { return m_values[-1]; } // value of a*x
numeral const& weight() const { return m_values[-1]; } // value of a*x
numeral& weight(int i) { return m_values[-2-i]; } // value of b_i*x for 0 <= i < current inequality.
numeral const& weight(int i) const { return m_values[-2-i]; } // value of b_i*x
numeral& operator[](unsigned i) { return m_values[i]; } // value of x_i
numeral const& operator[](unsigned i) const { return m_values[i]; } // value of x_i
numeral const* operator()() const { return m_values; }
};
vector<num_vector> m_ineqs; // set of asserted inequalities
svector<bool> m_iseq; // inequalities that are equalities
num_vector m_store; // store of vectors
svector<offset_t> m_basis; // vector of current basis
svector<offset_t> m_free_list; // free list of unused storage
svector<offset_t> m_active; // active set
svector<offset_t> m_sos; // set of support
svector<offset_t> m_zero; // zeros
passive* m_passive; // passive set
passive2* m_passive2; // passive set
volatile bool m_cancel;
stats m_stats;
index* m_index; // index of generated vectors
unsigned_vector m_ints; // indices that can be both positive and negative
unsigned m_current_ineq;
bool m_use_support; // parameter: (associativity) resolve only against vectors that are initially in basis.
bool m_use_ordered_support; // parameter: (commutativity) resolve in order
bool m_use_ordered_subsumption; // parameter
class iterator {
hilbert_basis const& hb;
unsigned m_idx;
public:
iterator(hilbert_basis const& hb, unsigned idx): hb(hb), m_idx(idx) {}
offset_t operator*() const { return hb.m_basis[m_idx]; }
iterator& operator++() { ++m_idx; return *this; }
iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
bool operator==(iterator const& it) const {return m_idx == it.m_idx; }
bool operator!=(iterator const& it) const {return m_idx != it.m_idx; }
};
static offset_t mk_invalid_offset();
static bool is_invalid_offset(offset_t offs);
lbool saturate(num_vector const& ineq, bool is_eq);
lbool saturate_orig(num_vector const& ineq, bool is_eq);
void init_basis();
void select_inequality();
unsigned get_num_nonzeros(num_vector const& ineq);
unsigned get_ineq_product(num_vector const& ineq);
numeral get_ineq_diff(num_vector const& ineq);
void add_unit_vector(unsigned i, numeral const& e);
unsigned get_num_vars() const;
numeral get_weight(values const & val, num_vector const& ineq) const;
bool is_geq(values const& v, values const& w) const;
bool is_abs_geq(numeral const& v, numeral const& w) const;
bool is_subsumed(offset_t idx);
bool is_subsumed(offset_t i, offset_t j) const;
void recycle(offset_t idx);
bool can_resolve(offset_t i, offset_t j, bool check_sign) const;
sign_t get_sign(offset_t idx) const;
bool add_goal(offset_t idx);
offset_t alloc_vector();
void resolve(offset_t i, offset_t j, offset_t r);
iterator begin() const { return iterator(*this,0); }
iterator end() const { return iterator(*this, m_basis.size()); }
class vector_lt_t;
bool vector_lt(offset_t i, offset_t j) const;
values vec(offset_t offs) const;
void display(std::ostream& out, offset_t o) const;
void display(std::ostream& out, values const & v) const;
void display_ineq(std::ostream& out, num_vector const& v, bool is_eq) const;
public:
hilbert_basis();
~hilbert_basis();
void reset();
void set_use_support(bool b) { m_use_support = b; }
void set_use_ordered_support(bool b) { m_use_ordered_support = b; }
void set_use_ordered_subsumption(bool b) { m_use_ordered_subsumption = b; }
// add inequality v*x >= 0
// add inequality v*x <= 0
// add equality v*x = 0
void add_ge(rational_vector const& v);
void add_le(rational_vector const& v);
void add_eq(rational_vector const& v);
// add inequality v*x >= b
// add inequality v*x <= b
// add equality v*x = b
void add_ge(rational_vector const& v, rational const& b);
void add_le(rational_vector const& v, rational const& b);
void add_eq(rational_vector const& v, rational const& b);
void set_is_int(unsigned var_index);
bool get_is_int(unsigned var_index) const;
lbool saturate();
unsigned get_basis_size() const { return m_basis.size(); }
void get_basis_solution(unsigned i, rational_vector& v, bool& is_initial);
unsigned get_num_ineqs() const { return m_ineqs.size(); }
void get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq);
void set_cancel(bool f) { m_cancel = f; }
void display(std::ostream& out) const;
void collect_statistics(statistics& st) const;
void reset_statistics();
};
#endif

View file

@ -1,233 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
horn_subsume_model_converter.cpp
Abstract:
Model converter for redundant Horn clauses.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-16
Revision History:
--*/
#include "horn_subsume_model_converter.h"
#include "var_subst.h"
#include "ast_pp.h"
#include "model_smt2_pp.h"
#include "bool_rewriter.h"
#include "th_rewriter.h"
#include "for_each_expr.h"
#include "well_sorted.h"
void horn_subsume_model_converter::insert(app* head, expr* body) {
m_delay_head.push_back(head);
m_delay_body.push_back(body);
}
void horn_subsume_model_converter::insert(app* head, unsigned sz, expr* const* body) {
expr_ref b(m);
bool_rewriter(m).mk_and(sz, body, b);
insert(head, b.get());
}
bool horn_subsume_model_converter::mk_horn(
app* head, expr* body, func_decl_ref& pred, expr_ref& body_res) {
expr_ref_vector conjs(m), subst(m);
ptr_vector<sort> sorts, sorts2;
var_subst vs(m, false);
if (!is_uninterp(head)) {
return false;
}
pred = head->get_decl();
unsigned arity = head->get_num_args();
get_free_vars(head, sorts);
get_free_vars(body, sorts);
if (arity == 0 && sorts.empty()) {
body_res = body;
return true;
}
svector<symbol> names;
for (unsigned i = 0; i < sorts.size(); ++i) {
if (!sorts[i]) {
sorts[i] = m.mk_bool_sort();
}
names.push_back(symbol(i));
}
names.reverse();
sorts.reverse();
conjs.push_back(body);
for (unsigned i = 0; i < arity; ++i) {
expr* arg = head->get_arg(i);
var_ref v(m);
v = m.mk_var(sorts.size()+i, m.get_sort(arg));
if (is_var(arg)) {
unsigned w = to_var(arg)->get_idx();
if (w >= subst.size()) {
subst.resize(w+1);
}
if (subst[w].get()) {
conjs.push_back(m.mk_eq(v, subst[w].get()));
}
else {
subst[w] = v;
}
}
else {
conjs.push_back(m.mk_eq(v, arg));
}
}
expr_ref body_expr(m);
body_expr = m.mk_and(conjs.size(), conjs.c_ptr());
// substitute variables directly.
if (!subst.empty()) {
expr_ref tmp(body_expr);
vs(tmp, subst.size(), subst.c_ptr(), body_expr);
}
if (sorts.empty()) {
SASSERT(subst.empty());
body_res = body_expr;
}
else {
body_res = m.mk_exists(sorts.size(), sorts.c_ptr(), names.c_ptr(), body_expr.get());
m_rewrite(body_res);
}
TRACE("mc",
tout << mk_pp(head, m) << " :- " << mk_pp(body, m) << "\n";
tout << pred->get_name() << " :- " << mk_pp(body_res.get(), m) << "\n";);
return true;
}
bool horn_subsume_model_converter::mk_horn(
expr* clause, func_decl_ref& pred, expr_ref& body) {
ptr_vector<sort> sorts;
// formula is closed.
DEBUG_CODE(get_free_vars(clause, sorts); SASSERT(sorts.empty()););
while (is_quantifier(clause) && to_quantifier(clause)->is_forall()) {
quantifier* q = to_quantifier(clause);
clause = q->get_expr();
}
expr* e1, *e2;
if (m.is_implies(clause, e1, e2)) {
if (!is_uninterp(e2)) {
return false;
}
return mk_horn(to_app(e2), e1, pred, body);
}
else if (m.is_or(clause)) {
// todo?
return false;
}
else {
return false;
}
}
void horn_subsume_model_converter::add_default_proc::operator()(app* n) {
//
// predicates that have not been assigned values
// in the Horn model are assumed false.
//
if (m.is_bool(n) &&
!m_md->has_interpretation(n->get_decl()) &&
(n->get_family_id() == null_family_id)) {
TRACE("mc", tout << "adding: " << n->get_decl()->get_name() << "\n";);
if (n->get_decl()->get_arity() == 0) {
m_md->register_decl(n->get_decl(), m.mk_false());
}
else {
func_interp* fi = alloc(func_interp, m, n->get_decl()->get_arity());
fi->set_else(m.mk_false());
m_md->register_decl(n->get_decl(), fi);
}
}
}
void horn_subsume_model_converter::add_default_false_interpretation(expr* e, model_ref& md) {
add_default_proc proc(m, md);
for_each_expr(proc, e);
}
void horn_subsume_model_converter::operator()(model_ref& mr) {
func_decl_ref pred(m);
expr_ref body_res(m);
for (unsigned i = 0; i < m_delay_head.size(); ++i) {
VERIFY(mk_horn(m_delay_head[i].get(), m_delay_body[i].get(), pred, body_res));
insert(pred.get(), body_res.get());
}
m_delay_head.reset();
m_delay_body.reset();
TRACE("mc", tout << m_funcs.size() << "\n"; model_smt2_pp(tout, m, *mr, 0););
for (unsigned i = m_funcs.size(); i > 0; ) {
--i;
func_decl* h = m_funcs[i].get();
expr_ref body(m_bodies[i].get(), m);
unsigned arity = h->get_arity();
add_default_false_interpretation(body, mr);
SASSERT(m.is_bool(body));
TRACE("mc", tout << "eval: " << h->get_name() << "\n" << mk_pp(body, m) << "\n";);
expr_ref tmp(body);
mr->eval(tmp, body);
TRACE("mc", tout << "to:\n" << mk_pp(body, m) << "\n";);
if (arity == 0) {
expr* e = mr->get_const_interp(h);
if (e) {
body = m.mk_or(e, body);
}
m_rewrite(body);
mr->register_decl(h, body);
}
else {
func_interp* f = mr->get_func_interp(h);
if (f) {
expr* e = f->get_else();
body = m.mk_or(e, body);
}
else {
f = alloc(func_interp, m, arity);
mr->register_decl(h, f);
}
m_rewrite(body);
f->set_else(body);
}
}
}
model_converter* horn_subsume_model_converter::translate(ast_translation & translator) {
horn_subsume_model_converter* mc = alloc(horn_subsume_model_converter, translator.to());
for (unsigned i = 0; i < m_funcs.size(); ++i) {
mc->insert(translator(m_funcs[i].get()), translator(m_bodies[i].get()));
}
return mc;
}

View file

@ -1,83 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
horn_subsume_model_converter.h
Abstract:
Model converter for redundant Horn clauses.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-16
Revision History:
Notes:
Subsumption transformation (remove Horn clause):
P(x) :- Body(x,y) Rules
----------------------------
Rules
Model converter:
P(x) := P(x) or (exists y. Body(x,y))
--*/
#ifndef _HORN_SUBSUME_MODEL_CONVERTER_H_
#define _HORN_SUBSUME_MODEL_CONVERTER_H_
#include "model_converter.h"
#include "th_rewriter.h"
class horn_subsume_model_converter : public model_converter {
ast_manager& m;
func_decl_ref_vector m_funcs;
expr_ref_vector m_bodies;
th_rewriter m_rewrite;
app_ref_vector m_delay_head;
expr_ref_vector m_delay_body;
void add_default_false_interpretation(expr* e, model_ref& md);
struct add_default_proc {
ast_manager& m;
model_ref& m_md;
add_default_proc(ast_manager& m, model_ref& md): m(m), m_md(md) {}
void operator()(app* n);
void operator()(expr* n) {}
};
public:
horn_subsume_model_converter(ast_manager& m):
m(m), m_funcs(m), m_bodies(m), m_rewrite(m),
m_delay_head(m), m_delay_body(m) {}
bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body);
bool mk_horn(app* head, expr* body, func_decl_ref& pred, expr_ref& body_res);
void insert(app* head, expr* body);
void insert(app* head, unsigned sz, expr* const* body);
void insert(func_decl* p, expr* body) { m_funcs.push_back(p); m_bodies.push_back(body); }
virtual void operator()(model_ref& m);
virtual model_converter * translate(ast_translation & translator);
ast_manager& get_manager() { return m; }
};
#endif

View file

@ -1,157 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
model2expr.cpp
Abstract:
Convert model to logical formula that forces it.
Author:
Nikolaj Bjorner (nbjorner) 2012-09-17
Revision History:
--*/
#include "model2expr.h"
#include "for_each_ast.h"
#include "bool_rewriter.h"
#include "var_subst.h"
struct for_each_symbol_proc {
symbol_set& m_symbols;
for_each_symbol_proc(symbol_set& syms): m_symbols(syms) {}
void operator()(func_decl* n) {
m_symbols.insert(n->get_name());
}
void operator()(quantifier* n) {
for (unsigned i = 0; i < n->get_num_decls(); ++i) {
m_symbols.insert(n->get_decl_name(i));
}
}
void operator()(var* n) {}
void operator()(sort* s) {}
void operator()(app* a) {}
};
void mk_fresh_name::add(ast* a) {
for_each_symbol_proc proc(m_symbols);
for_each_ast(proc, a);
}
symbol mk_fresh_name::next() {
for (; ; ++m_num) {
for(; m_char <= 'Z'; ++m_char) {
std::stringstream _name;
_name << m_char;
if (m_num > 0) _name << m_num;
++m_char;
symbol name(_name.str().c_str());
if (!m_symbols.contains(name)) {
return name;
}
}
m_char = 'A';
}
}
static void mk_entry_cond(unsigned arity, func_entry const* entry, expr_ref& result) {
ast_manager& m = result.get_manager();
expr_ref_vector conjs(m);
for (unsigned i = 0; i < arity; ++i) {
expr* e = entry->get_arg(i);
if (is_var(e) && to_var(e)->get_idx() == i) {
// no-op
}
else {
conjs.push_back(m.mk_eq(m.mk_var(i, m.get_sort(e)), e));
}
}
bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result);
}
void model2expr(model& md, expr_ref& result) {
ast_manager& m = result.get_manager();
expr_ref_vector conjs(m);
expr_ref tmp(m);
unsigned sz;
sz = md.get_num_constants();
for (unsigned i = 0; i < sz; ++i) {
func_decl* c = md.get_constant(i);
expr* v = md.get_const_interp(c);
conjs.push_back(m.mk_eq(m.mk_const(c), v));
}
sz = md.get_num_functions();
for (unsigned i = 0; i < sz; ++i) {
func_decl* f = md.get_function(i);
func_interp* fi = md.get_func_interp(f);
// Register names.
mk_fresh_name fresh_name;
unsigned num_entries = fi->num_entries();
fresh_name.add(f);
for (unsigned j = 0; j < num_entries; ++j) {
func_entry const* entry = fi->get_entry(j);
fresh_name.add(entry->get_result());
for (unsigned k = 0; k < f->get_arity(); ++k) {
fresh_name.add(entry->get_arg(k));
}
}
expr_ref func(m), cond(m);
expr_ref_vector args(m);
for (unsigned j = 0; j < f->get_arity(); ++j) {
args.push_back(m.mk_var(j, f->get_domain(j)));
}
func = m.mk_app(f, args.size(), args.c_ptr());
if (fi->is_partial()) {
if (num_entries == 0) {
continue;
}
mk_entry_cond(f->get_arity(), fi->get_entry(num_entries-1), cond);
tmp = m.mk_implies(cond, m.mk_eq(func, fi->get_entry(num_entries-1)->get_result()));
for (unsigned j = num_entries-1; j > 0; ) {
--j;
mk_entry_cond(f->get_arity(), fi->get_entry(j), cond);
tmp = m.mk_ite(cond, m.mk_eq(func, fi->get_entry(j)->get_result()), tmp);
}
}
else {
fresh_name.add(fi->get_else());
tmp = fi->get_else();
for (unsigned j = num_entries; j > 0; ) {
--j;
mk_entry_cond(f->get_arity(), fi->get_entry(j), cond);
tmp = m.mk_ite(cond, fi->get_entry(j)->get_result(), tmp);
}
tmp = m.mk_eq(func, tmp);
}
ptr_vector<sort> sorts;
expr_ref_vector rev_vars(m);
svector<symbol> names;
unsigned sz = f->get_arity();
for (unsigned j = 0; j < sz; ++j) {
sorts.push_back(f->get_domain(j));
rev_vars.push_back(m.mk_var(sz-j-1, f->get_domain(j)));
names.push_back(fresh_name.next());
}
if (f->get_arity() > 0) {
var_subst vs(m, false);
vs(tmp, rev_vars.size(), rev_vars.c_ptr(), tmp);
tmp = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp);
}
conjs.push_back(tmp);
}
bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result);
}

View file

@ -1,45 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
model2expr.h
Abstract:
Convert model to logical formula that forces it.
Author:
Nikolaj Bjorner (nbjorner) 2012-09-17
Revision History:
--*/
#ifndef _MODEL2EXPR_H_
#define _MODEL2EXPR_H_
#include"model.h"
void model2expr(model& m, expr_ref& result);
inline void model2expr(model_ref& md, expr_ref& result) { model2expr(*md.get(), result); }
// TODO: move
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
class mk_fresh_name {
symbol_set m_symbols;
char m_char;
unsigned m_num;
public:
mk_fresh_name(): m_char('A'), m_num(0) {}
void add(ast* a);
void add(symbol const& s) { m_symbols.insert(s); }
symbol next();
bool contains(symbol const& s) const { return m_symbols.contains(s); }
};
#endif /* _MODEL2EXPR_H_ */

View file

@ -1,86 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
replace_proof_converter.cpp
Abstract:
Proof converter that replaces asserted by sub-proof.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-16
Revision History:
--*/
#include "replace_proof_converter.h"
#include "expr_functors.h"
#include "ast_pp.h"
#include "for_each_expr.h"
/**
\brief Replace expressions by other expressions.
replace_map is caching, so inserting src |-> dst has no effect if
src is a sub-expression of something that has already been visited.
The assumption is that proof replacements are inserted into
the replace_proof_converter in the order that they are introduced, so
there are no such clashes.
map_proc is used as expr_replacer behaves differently
when proof mode is turned on.
*/
class replace_map : public map_proc {
public:
replace_map(ast_manager& m): map_proc(m) {}
void insert(expr* src, expr* dst) {
m_map.insert(src, dst, 0);
}
void operator()(var* v) { visit(v); }
void operator()(app* a) { if (!get_expr(a)) { reconstruct(a); } }
void operator()(quantifier* q) { visit(q); }
void apply(expr_ref& e) {
for_each_expr(*this, e);
e = get_expr(e);
}
};
void replace_proof_converter::operator()(ast_manager & m, unsigned num_source,
proof * const * source, proof_ref & result) {
SASSERT(num_source == 1);
replace_map replace(m);
proof_ref p(m);
expr_ref tmp(source[0], m), e(m), f(m);
// apply the substitution to the prefix before inserting it.
for (unsigned i = 0; i < m_proofs.size(); ++i) {
p = m_proofs[i].get();
e = p;
replace.apply(e);
f = m.mk_asserted(m.get_fact(p));
replace.insert(f, e);
TRACE("proof_converter", tout << f->get_id() << " " << mk_pp(f, m) <<
"\n|-> " << mk_pp(e, m) << "\n";);
}
replace.apply(tmp);
TRACE("proof_converter", tout << mk_pp(source[0], m) << "\n";
tout << mk_pp(tmp.get(), m) << "\n";);
result = to_app(tmp);
}
proof_converter * replace_proof_converter::translate(ast_translation & translator) {
replace_proof_converter* rp = alloc(replace_proof_converter, m);
for (unsigned i = 0; i < m_proofs.size(); ++i) {
rp->insert(translator(m_proofs[i].get()));
}
return rp;
}

View file

@ -1,50 +0,0 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
replace_proof_converter.h
Abstract:
Proof converter to replace asserted leaves by proofs.
Given a proof P with occurrences of [asserted fml]
Replace [asserted fml] by proofs whose conclusions are fml.
Author:
Nikolaj Bjorner (nbjorner) 2012-9-16
Revision History:
--*/
#ifndef _REPLACE_PROOF_CONVERTER_H_
#define _REPLACE_PROOF_CONVERTER_H_
#include "proof_converter.h"
class replace_proof_converter : public proof_converter {
ast_manager& m;
proof_ref_vector m_proofs;
public:
replace_proof_converter(ast_manager& m): m(m), m_proofs(m) {}
virtual ~replace_proof_converter() {}
virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result);
virtual proof_converter * translate(ast_translation & translator);
void insert(proof* p) { m_proofs.push_back(p); }
ast_manager& get_manager() { return m; }
// run the replacements the inverse direction.
void invert() { m_proofs.reverse(); }
};
#endif

View file

@ -1,55 +0,0 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
scoped_proof.h
Abstract:
Scoped proof environments. Toggles enabling proofs.
Author:
Nikolaj Bjorner (nbjorner) 2013-08-28
Revision History:
--*/
#ifndef _SCOPED_PROOF__H_
#define _SCOPED_PROOF_H_
#include "ast.h"
class scoped_proof_mode {
ast_manager& m;
proof_gen_mode m_mode;
public:
scoped_proof_mode(ast_manager& m, proof_gen_mode mode): m(m) {
m_mode = m.proof_mode();
m.toggle_proof_mode(mode);
}
~scoped_proof_mode() {
m.toggle_proof_mode(m_mode);
}
};
class scoped_proof : public scoped_proof_mode {
public:
scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_FINE) {}
};
class scoped_no_proof : public scoped_proof_mode {
public:
scoped_no_proof(ast_manager& m): scoped_proof_mode(m, PGM_DISABLED) {}
};
class scoped_restore_proof : public scoped_proof_mode {
public:
scoped_restore_proof(ast_manager& m): scoped_proof_mode(m, m.proof_mode()) {}
};
#endif

View file

@ -1,871 +0,0 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
skip_list_base.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-10-01.
Revision History:
WARNING: IT IS NOT SAFE TO STORE KEYS, VALUES in the SKIP_LIST that need non-default constructors/destructors.
--*/
#ifndef _SKIP_LIST_BASE_H_
#define _SKIP_LIST_BASE_H_
#include<memory.h>
#include"util.h"
#include"memory_manager.h"
#include"small_object_allocator.h"
#include"trace.h"
#ifdef _MSC_VER
#pragma warning(disable : 4200)
#endif
/*
This file defines a base class for implementing skip-list like data-structures.
This base class is relies on a manager for providing some basic services.
The manager is a template parameter.
A Skip-list manager is responsible for:
- Providing primitives for allocating/deallocating memory
void * allocate(size_t size);
void deallocate(size_t size, void* p);
- Generating random skip-list levels efficiently
unsigned random_level(unsigned max_level);
- Call-backs that will be invoked when a reference for a "value" stored in the skip-list is incremented/decremented.
void inc_ref_eh(value const & v);
void dec_ref_eh(value const & h);
*/
/**
\brief Base class for generating random_levels.
*/
class random_level_manager {
#define SL_BITS_IN_RANDOM 16
unsigned m_random_data;
unsigned m_random_bits:16;
unsigned m_random_left:16;
unsigned random_value() {
return ((m_random_data = m_random_data * 214013L + 2531011L) >> 16) & 0xffff;
}
void init_random() {
m_random_data = 0;
m_random_bits = random_value();
m_random_left = SL_BITS_IN_RANDOM/2;
}
public:
random_level_manager() {
init_random();
}
unsigned random_level(unsigned max_level) {
unsigned level = 1;
unsigned b;
do {
b = m_random_bits&3;
if (!b)
level++;
m_random_bits >>= 2;
m_random_left--;
if (m_random_left == 0) {
m_random_bits = random_value();
m_random_left = SL_BITS_IN_RANDOM/2;
}
} while (!b);
return (level > max_level ? max_level : level);
}
};
/**
\brief Basic skip-list manager.
The class is parametrized by the Value type that is stored in the skip-list.
*/
template<typename Value>
class sl_manager_base : public random_level_manager {
typedef Value value;
small_object_allocator m_alloc;
public:
void * allocate(size_t size) {
return m_alloc.allocate(size);
}
void deallocate(size_t size, void* p) {
m_alloc.deallocate(size, p);
}
void inc_ref_eh(value const & v) {
/* do nothing */
}
void dec_ref_eh(value const & h) {
/* do nothing */
}
};
#define SL_SIZE_NUM_BITS 12
#define SL_CAPACITY_NUM_BITS SL_SIZE_NUM_BITS
#define SL_MAX_CAPACITY ((1 << SL_SIZE_NUM_BITS) - 1)
#define SL_LEVEL_NUM_BITS 8
#define SL_MAX_LEVEL ((1 << SL_LEVEL_NUM_BITS) - 1)
COMPILE_TIME_ASSERT(SL_SIZE_NUM_BITS == SL_CAPACITY_NUM_BITS);
COMPILE_TIME_ASSERT(SL_SIZE_NUM_BITS + SL_CAPACITY_NUM_BITS + SL_LEVEL_NUM_BITS == 32);
/**
\brief Base (template) class for implementing skip-list like data-structures where
entries are stored in buckets to improve cache behavior.
The Traits template parameter must provide:
- a definition for the class Traits::manager
- a definition for the class Traits::entry which provides:
- a definition for the types key and value
- the methods:
key const & begin_key() const
key const & end_key() const
value const & val() const
void set_begin_key(key const & k)
void set_end_key(key const & k)
void set_val(value const & v)
void display(ostream & out) const
- the maximal number of levels Traits::max_level
- the maximal capacity of each bucket Traits::max_capacity
- the initial capacity of the first bucket Traits::initial_capacity
- flag for reference counting support Traits::ref_count. If this flag is true
the methods inc_ref_eh and dec_ref_eh in the manager object will be invoked.
- the methods
bool lt(key const & k1, key const & k2)
bool eq(key const & k1, key const & k2)
bool val_eq(value const & v1, value const & v2)
key succ(key const & k)
key pred(key const & k)
*/
template<typename Traits>
class skip_list_base : protected Traits {
protected:
typedef typename Traits::entry entry;
public:
typedef typename Traits::manager manager;
typedef typename entry::key key;
typedef typename entry::value value;
struct bucket {
unsigned m_size:SL_SIZE_NUM_BITS; //!< number of entries stored in the bucket.
unsigned m_capacity:SL_CAPACITY_NUM_BITS; //!< capacity (number of entries) that can be stored in the bucket.
unsigned m_level:SL_LEVEL_NUM_BITS;
char m_extra[0];
static unsigned get_obj_size(unsigned num_lvls, unsigned capacity) {
return sizeof(bucket) + num_lvls*sizeof(bucket*) + capacity*sizeof(entry);
}
entry * get_entries() { return reinterpret_cast<entry*>(m_extra); }
entry const * get_entries() const { return reinterpret_cast<entry const *>(m_extra); }
bucket ** next_vect() { return reinterpret_cast<bucket**>(get_entries() + m_capacity); }
bucket * const * next_vect() const { return reinterpret_cast<bucket* const *>(get_entries() + m_capacity); }
bucket(unsigned lvl, unsigned capacity = Traits::max_capacity):
m_size(0),
m_capacity(capacity),
m_level(lvl) {
memset(next_vect(), 0, sizeof(bucket*)*lvl);
}
unsigned level() const { return m_level; }
unsigned size() const { return m_size; }
unsigned capacity() const { return m_capacity; }
bool empty() const { return size() == 0; }
void set_size(unsigned sz) { m_size = sz; }
void shrink(unsigned delta) { m_size -= delta; }
void expand(unsigned delta) { m_size += delta; }
entry & first_entry() { SASSERT(!empty()); return get_entries()[0]; }
entry & last_entry() { SASSERT(!empty()); return get_entries()[size() - 1]; }
entry const & first_entry() const { SASSERT(!empty()); return get_entries()[0]; }
entry const & last_entry() const { SASSERT(!empty()); return get_entries()[size() - 1]; }
entry const & get(unsigned idx) const { SASSERT(idx < size()); return get_entries()[idx]; }
entry & get(unsigned idx) { SASSERT(idx < size()); return get_entries()[idx]; }
void set(unsigned idx, entry const & e) { SASSERT(idx < capacity()); get_entries()[idx] = e; }
bucket * get_next(unsigned idx) const { return next_vect()[idx]; }
void set_next(unsigned idx, bucket * bt) { SASSERT(idx < level()); next_vect()[idx] = bt; }
};
// Only the header bucket has zero entries.
bucket * m_header;
bucket * first_bucket() const {
return m_header->get_next(0);
}
#ifdef Z3DEBUG
/**
\brief (debugging only) Return the predecessor bucket of the given bucket.
\pre bt != m_header, and bt is a bucket of the list.
*/
bucket * pred_bucket(bucket * bt) const {
SASSERT(bt != m_header);
bucket * curr = m_header;
while (curr->get_next(0) != bt) {
curr = curr->get_next(0);
SASSERT(curr != 0); // bt is not in the list
}
return curr;
}
#endif
bool lt(key const & k1, key const & k2) const { return Traits::lt(k1, k2); }
bool gt(key const & k1, key const & k2) const { return lt(k2, k1); }
bool geq(key const & k1, key const & k2) const { return !lt(k1, k2); }
bool leq(key const & k1, key const & k2) const { return !gt(k1, k2); }
/**
\brief Create a new bucket of the given level.
*/
static bucket * mk_bucket(manager & m, unsigned lvl, unsigned capacity = Traits::max_capacity) {
void * mem = m.allocate(bucket::get_obj_size(lvl, capacity));
return new (mem) bucket(lvl, capacity);
}
static bucket * mk_header(manager & m, unsigned lvl) {
return mk_bucket(m, lvl, 0);
}
static void inc_ref(manager & m, value const & v) {
if (Traits::ref_count)
m.inc_ref_eh(v);
}
static void dec_ref(manager & m, value const & v) {
if (Traits::ref_count)
m.dec_ref_eh(v);
}
/**
\brief Invoke dec_ref_eh for each value stored in the bucket.
*/
static void dec_ref(manager & m, bucket * bt) {
if (Traits::ref_count) {
unsigned sz = bt->size();
for (unsigned i = 0; i < sz; i++)
m.dec_ref_eh(bt->get(i).val());
}
}
/**
\brief Deallocate the given bucket.
\remark This method invokes dec_ref_eh for each value in the bucket.
*/
template<bool DecRef>
static void deallocate_bucket(manager & m, bucket * bt) {
if (DecRef)
dec_ref(m, bt);
unsigned sz = bucket::get_obj_size(bt->level(), bt->capacity());
bt->~bucket();
m.deallocate(sz, bt);
}
/**
\brief Deallocate all buckets in the skip list.
\remark This method invokes dec_ref_eh for each value in the list.
*/
template<bool DecRef>
void deallocate_list(manager & m) {
bucket * curr = m_header;
while (curr != 0) {
bucket * old = curr;
curr = curr->get_next(0);
deallocate_bucket<DecRef>(m, old);
}
}
#ifdef Z3DEBUG
/**
\brief Check the following property
for all i \in [0, b->level()) . pred_vect[i]->get_next(i) == b
*/
bool check_pred_vect(bucket * bt, bucket * pred_vect[]) {
if (bt == 0)
return true;
for (unsigned i = 0; i < bt->level(); i++) {
SASSERT(pred_vect[i]->get_next(i) == bt);
}
return true;
}
#endif
/**
\brief Delete the given buffer and update the forward/next pointer of the buckets in pred_vect.
\remark This method invokes dec_ref_eh for each value in the bucket.
*/
void del_bucket(manager & m, bucket * bt, bucket * pred_vect[]) {
SASSERT(check_pred_vect(bt, pred_vect));
for (unsigned i = 0; i < bt->level(); i++)
pred_vect[i]->set_next(i, bt->get_next(i));
deallocate_bucket<true>(m, bt);
}
/**
\brief Update the \c pred_vect vector from levels [0, bt->level()).
That is, bt will be now the "predecessor" for these levels.
*/
static void update_predecessor_vector(bucket * pred_vect [], bucket * bt) {
unsigned lvl = bt->level();
for (unsigned i = 0; i < lvl; i++) {
pred_vect[i] = bt;
}
}
/**
\brief Similar to the previous method, but the updated vector is stored in new_pred_vect.
*/
void update_predecessor_vector(bucket * pred_vect[], bucket * bt, bucket * new_pred_vect[]) {
unsigned bt_lvl = bt->level();
for (unsigned i = 0; i < bt_lvl; i++) {
new_pred_vect[i] = bt;
}
unsigned list_lvl = level();
for (unsigned i = bt_lvl; i < list_lvl; i++) {
new_pred_vect[i] = pred_vect[i];
}
}
/**
\brief Return the list level.
*/
unsigned level() const {
return m_header->level();
}
/**
\brief Expand/Increase the number of levels in the header.
*/
void expand_header(manager & m, unsigned new_lvl) {
SASSERT(new_lvl > level());
bucket * new_header = mk_header(m, new_lvl);
// copy forward pointers of the old header.
unsigned old_lvl = level();
for (unsigned i = 0; i < old_lvl; i++)
new_header->set_next(i, m_header->get_next(i));
// update header
deallocate_bucket<false>(m, m_header);
m_header = new_header;
}
/**
\brief Increase list level to lvl if lvl > level()
*/
void update_list_level(manager & m, unsigned lvl) {
if (lvl > level()) {
expand_header(m, lvl);
}
}
/**
\brief Increase list level (and store m_header in the new levels in pred_vect) if lvl > level().
*/
void update_list_level(manager & m, unsigned lvl, bucket * pred_vect[]) {
if (lvl > level()) {
bucket * old_header = m_header;
unsigned old_lvl = m_header->level();
expand_header(m, lvl);
for (unsigned i = 0; i < old_lvl; i++) {
if (pred_vect[i] == old_header)
pred_vect[i] = m_header;
}
for (unsigned i = old_lvl; i < lvl; i++) {
pred_vect[i] = m_header;
}
SASSERT(level() == lvl);
}
}
/**
\brief Add first entry to the list.
\remark This method will invoke inc_ref_eh for e.val()
*/
void insert_first_entry(manager & m, entry const & e) {
unsigned lvl = m.random_level(Traits::max_level);
bucket * new_bucket = mk_bucket(m, lvl, Traits::initial_capacity);
update_list_level(m, lvl);
for (unsigned i = 0; i < lvl; i++) {
m_header->set_next(i, new_bucket);
}
inc_ref(m, e.val());
new_bucket->set_size(1);
new_bucket->set(0, e);
}
/**
\brief Expand the capacity of the first-bucket in a skip-list with only one bucket.
This method assumes the capacity of the first-bucket < Traits::max_capacity
*/
void expand_first_bucket(manager & m) {
bucket * f = first_bucket();
SASSERT(f != 0);
SASSERT(f->get_next(0) == 0);
SASSERT(f->capacity() < Traits::max_capacity);
unsigned old_capacity = f->capacity();
SASSERT(old_capacity > 0);
unsigned new_capacity = old_capacity * 2;
if (new_capacity > Traits::max_capacity)
new_capacity = Traits::max_capacity;
unsigned lvl = f->level();
bucket * new_f = mk_bucket(m, lvl, new_capacity);
unsigned sz = f->size();
new_f->set_size(sz);
for (unsigned i = 0; i < sz; i++)
new_f->set(i, f->get(i));
for (unsigned i = 0; i < lvl; i++)
m_header->set_next(i, new_f);
deallocate_bucket<false>(m, f);
SASSERT(first_bucket() == new_f);
}
/**
\brief Create a new bucket and divide the elements in bt between bt and the new bucket.
*/
void splice(manager & m, bucket * bt, bucket * pred_vect[]) {
SASSERT(bt->capacity() == Traits::max_capacity);
unsigned bt_lvl = bt->level();
unsigned new_bucket_lvl = m.random_level(Traits::max_level);
bucket * new_bucket = mk_bucket(m, new_bucket_lvl);
update_list_level(m, new_bucket_lvl, pred_vect);
unsigned _lvl = std::min(bt_lvl, new_bucket_lvl);
for (unsigned i = 0; i < _lvl; i++) {
new_bucket->set_next(i, bt->get_next(i));
bt->set_next(i, new_bucket);
}
for (unsigned i = bt_lvl; i < new_bucket_lvl; i++) {
new_bucket->set_next(i, pred_vect[i]->get_next(i));
pred_vect[i]->set_next(i, new_bucket);
}
unsigned old_size = bt->size();
SASSERT(old_size >= 2);
unsigned mid = old_size/2;
new_bucket->set_size(old_size - mid);
unsigned i = mid;
unsigned j = 0;
for (; i < old_size; i++, j++) {
new_bucket->set(j, bt->get(i));
}
bt->set_size(mid);
SASSERT(!bt->empty());
SASSERT(!new_bucket->empty());
}
/**
\brief Open space at position idx. The number of entries in bt is increased by one.
\remark This method will *NOT* invoke inc_ref_eh
*/
void open_space(bucket * bt, unsigned idx) {
SASSERT(bt->size() < bt->capacity());
SASSERT(idx <= bt->size());
unsigned i = bt->size();
while (i > idx) {
bt->set(i, bt->get(i-1));
i--;
}
bt->expand(1);
}
/**
\brief Open two spaces at position idx. The number of entries in bt is increased by one.
\remark This method will *NOT* invoke inc_ref_eh
*/
void open_2spaces(bucket * bt, unsigned idx) {
SASSERT(bt->size() < bt->capacity() - 1);
SASSERT(idx <= bt->size());
unsigned i = bt->size() + 1;
unsigned end = idx + 1;
while (i > end) {
bt->set(i, bt->get(i-2));
i--;
}
bt->expand(2);
}
/**
\brief Delete entry at position idx.
\remark This method will invoke dec_ref_eh for the value stored in entry at position idx.
*/
void del_entry(manager & m, bucket * bt, unsigned idx) {
SASSERT(!bt->empty());
SASSERT(idx < bt->size());
dec_ref(m, bt->get(idx).val());
unsigned sz = bt->size();
for (unsigned i = idx; i < sz - 1; i++) {
bt->set(i, bt->get(i+1));
}
bt->shrink(1);
}
/**
\brief Create a copy of the skip list.
\remark This method will invoke inc_ref_eh for all values copied.
*/
void clone_core(manager & m, skip_list_base * new_list) const {
bucket * pred_vect[Traits::max_level];
unsigned lvl = level();
new_list->update_list_level(m, lvl);
bucket * new_header = new_list->m_header;
for (unsigned i = 0; i < lvl; i++)
pred_vect[i] = new_header;
bucket * curr = first_bucket();
while (curr != 0) {
unsigned curr_lvl = curr->level();
bucket * new_bucket = new_list->mk_bucket(m, curr_lvl, curr->capacity());
for (unsigned i = 0; i < curr_lvl; i++) {
pred_vect[i]->set_next(i, new_bucket);
pred_vect[i] = new_bucket;
}
unsigned curr_sz = curr->size();
for (unsigned i = 0; i < curr_sz; i++) {
entry const & curr_entry = curr->get(i);
inc_ref(m, curr_entry.val());
new_bucket->set(i, curr_entry);
}
new_bucket->set_size(curr_sz);
curr = curr->get_next(0);
}
}
public:
skip_list_base():
m_header(0) {
SASSERT(Traits::max_capacity >= 2);
SASSERT(Traits::initial_capacity >= 2);
SASSERT(Traits::initial_capacity <= Traits::max_capacity);
SASSERT(Traits::max_level >= 1);
SASSERT(Traits::max_capacity <= SL_MAX_CAPACITY);
SASSERT(Traits::max_level <= SL_MAX_LEVEL);
}
skip_list_base(manager & m):
m_header(0) {
SASSERT(Traits::max_capacity >= 2);
SASSERT(Traits::initial_capacity >= 2);
SASSERT(Traits::initial_capacity <= Traits::max_capacity);
SASSERT(Traits::max_level >= 1);
SASSERT(Traits::max_capacity <= SL_MAX_CAPACITY);
SASSERT(Traits::max_level <= SL_MAX_LEVEL);
init(m);
}
~skip_list_base() {
SASSERT(m_header == 0);
}
void deallocate(manager & m) {
deallocate_list<true>(m);
m_header = 0;
}
/**
\brief Deallocate the list but do not invoke dec_ref_eh.
*/
void deallocate_no_decref(manager & m) {
deallocate_list<false>(m);
m_header = 0;
}
/**
\brief Initialize a list that was created using the default constructor.
It can be used also to initialized a list deallocated using the method #deallocate.
*/
void init(manager & m) {
SASSERT(m_header == 0);
m_header = mk_header(m, 1);
}
/**
\brief Remove all elements from the skip-list.
*/
void reset(manager & m) {
deallocate_list<true>(m);
m_header = mk_header(m, 1);
}
/**
\brief Remove all elements from the skip-list without invoking dec_ref_eh.
*/
void reset_no_decref(manager & m) {
deallocate_list<false>(m);
m_header = mk_header(m, 1);
}
/**
\brief Return true if the list is empty.
*/
bool empty() const {
SASSERT(m_header != 0);
return first_bucket() == 0;
}
protected:
/**
\brief Return the position of the bucket in the skip list.
*/
unsigned get_bucket_idx(bucket const * bt) const {
bucket * curr = m_header;
unsigned pos = 0;
while (curr != 0) {
if (curr == bt)
return pos;
pos++;
curr = curr->get_next(0);
}
UNREACHABLE();
return pos;
}
/**
\brief Display the given entry.
*/
void display(std::ostream & out, entry const & e) const {
e.display(out);
}
/**
\brief Display a reference to the given bucket.
*/
void display_bucket_ref(std::ostream & out, bucket const * bt) const {
if (bt == 0)
out << "NIL";
else
out << "#" << get_bucket_idx(bt);
}
/**
\brief Display the predecessor vector.
*/
void display_predecessor_vector(std::ostream & out, bucket const * const pred_vect[]) const {
for (unsigned i = 0; i < level(); i++) {
out << i << ": ";
display_bucket_ref(out, pred_vect[i]);
if (pred_vect[i]) {
out << " -> ";
display_bucket_ref(out, pred_vect[i]->get_next(i));
}
out << "\n";
}
}
/**
\brief Display the successors of the given bucket.
*/
void display_successors(std::ostream & out, bucket const * bt) const {
out << "[";
for (unsigned i = 0; i < bt->level(); i++) {
if (i > 0) out << ", ";
display_bucket_ref(out, bt->get_next(i));
}
out << "]";
}
/**
\brief Display the given bucket.
*/
void display(std::ostream & out, bucket const * bt) const {
if (bt == 0) {
out << "NIL\n";
return;
}
out << "bucket ";
display_bucket_ref(out, bt);
out << ", capacity: " << bt->capacity() << "\n";
out << "successors: ";
display_successors(out, bt);
out << "\n";
out << "entries:\n";
for (unsigned i = 0; i < bt->size(); i++) {
display(out, bt->get(i));
out << "\n";
}
out << "----------\n";
}
public:
/**
\brief Dump the skip list for debugging purposes.
It assumes that key and value types implement operator <<.
*/
void display_physical(std::ostream & out) const {
out << "{\nskip-list level: " << m_header->level() << "\n";
bucket * curr = m_header;
while (curr != 0) {
display(out, curr);
curr = curr->get_next(0);
}
out << "}\n";
}
void display(std::ostream & out) const {
bucket * curr = m_header;
while (curr != 0) {
unsigned sz = curr->size();
for (unsigned i = 0; i < sz; i++) {
if (i > 0)
out << " ";
curr->get(i).display(out);
}
curr = curr->get_next(0);
}
}
protected:
/**
\brief Return true if bucket b2 can be reached from b1 following get_next(i) pointers
*/
bool is_reachable_at_i(bucket const * bt1, bucket const * bt2, unsigned i) const {
bucket * curr = bt1->get_next(i);
while (curr != 0) {
if (curr == bt2)
return true;
curr = curr->get_next(i);
}
return false;
}
protected:
static void display_size_info_core(std::ostream & out, unsigned cls_size) {
out << "sizeof root: " << cls_size << "\n";
out << "bucket max capacity: " << Traits::max_capacity << "\n";
out << "bucket max level: " << Traits::max_level << "\n";
out << "sizeof(bucket): " << sizeof(bucket) << " + " << sizeof(bucket*) << "*lvl + " << sizeof(entry) << "*capacity\n";
out << "sizeof(usual bucket): " << (sizeof(bucket) + sizeof(entry)*Traits::max_capacity) << " + " << sizeof(bucket*) << "*lvl\n";
out << "sizeof(max. bucket): " << (sizeof(bucket) + sizeof(entry)*Traits::max_capacity + sizeof(bucket*)*Traits::max_level) << "\n";
out << "sizeof(entry): " << sizeof(entry) << "\n";
out << "sizeof empty: " << cls_size + bucket::get_obj_size(1, 0) << "\n";;
out << "sizeof singleton: ["
<< (cls_size + bucket::get_obj_size(1, 0) + bucket::get_obj_size(1, Traits::initial_capacity)) << ", "
<< (cls_size +
bucket::get_obj_size(Traits::max_level, 0) +
bucket::get_obj_size(Traits::max_level, Traits::max_capacity)) << "]\n";
}
public:
/**
\brief Return true if skip-list has more than k buckets (not considering the header).
\remark This method is for debugging purposes.
*/
bool has_more_than_k_buckets(unsigned k) const {
bucket * curr = first_bucket();
while (curr != 0 && k > 0) {
curr = curr->get_next(0);
k--;
}
return curr != 0;
}
/**
\brief Return true if the skip-list has more than k entries.
*/
bool has_more_than_k_entries(unsigned k) const {
bucket * curr = first_bucket();
while (curr != 0 && k >= curr->size()) {
k -= curr->size();
curr = curr->get_next(0);
}
SASSERT(curr == 0 || curr->size() > k);
return curr != 0;
}
protected:
/**
\brief Return the amount of memory consumed by the list.
*/
unsigned memory_core(unsigned cls_size) const {
unsigned r = 0;
r += cls_size;
bucket * curr = m_header;
while (curr != 0) {
r += bucket::get_obj_size(curr->level(), curr->capacity());
curr = curr->get_next(0);
}
return r;
}
public:
/**
\brief Compress the buckets of the skip-list.
Make sure that all, but the last bucket, have at least \c load entries.
\remark If load > Traits::max_capacity, then it assumes load = Traits::max_capacity.
*/
void compress(manager & m, unsigned load = Traits::max_capacity/2) {
if (load > Traits::max_capacity)
load = Traits::max_capacity;
bucket * pred_vect[Traits::max_level];
update_predecessor_vector(pred_vect, m_header);
bucket * curr = first_bucket();
while (curr != 0) {
update_predecessor_vector(pred_vect, curr);
bucket * next = curr->get_next(0);
while (curr->size() < load && next != 0) {
// steal entries of the successor bucket.
unsigned deficit = load - curr->size();
unsigned next_size = next->size();
if (next_size <= deficit) {
for (unsigned i = 0, j = curr->size(); i < next_size; i++, j++) {
curr->set(j, next->get(i));
}
curr->expand(next_size);
bucket * new_next = next->get_next(0);
del_bucket(m, next, pred_vect);
next = new_next;
SASSERT(curr->size() <= load);
}
else {
for (unsigned i = 0, j = curr->size(); i < deficit; i++, j++) {
curr->set(j, next->get(i));
}
curr->expand(deficit);
for (unsigned i = deficit, j = 0; i < next_size; i++, j++) {
next->set(j, next->get(i));
}
next->set_size(next_size - deficit);
SASSERT(curr->size() == load);
}
}
curr = curr->get_next(0);
}
}
void swap(skip_list_base & other) {
bucket * tmp = m_header;
m_header = other.m_header;
other.m_header = tmp;
}
};
#endif