z3-src 503.2.1

Source distribution of the Z3 SMT solver, for use as a build dependency
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
/*++
Copyright (c) 2020 Microsoft Corporation

Module Name:

    bv_solver.h

Abstract:

    Theory plugin for bit-vectors

Author:

    Nikolaj Bjorner (nbjorner) 2020-08-30

--*/
#pragma once

#include "util/union_find.h"
#include "sat/smt/sat_th.h"
#include "sat/smt/bv_ackerman.h"
#include "ast/rewriter/bit_blaster/bit_blaster.h"

namespace euf {
    class solver;
}

namespace bv {

    struct lazy_mul {
        expr_ref_vector m_out;
        unsigned        m_bits;
        lazy_mul(app* a, expr_ref_vector& out):
            m_out(out), 
            m_bits(0) {
        }
    };




    class solver : public euf::th_euf_solver {
        typedef rational numeral;
        typedef euf::theory_var theory_var;
        typedef euf::theory_id theory_id;
        typedef sat::literal literal;
        typedef sat::bool_var bool_var;
        typedef sat::literal_vector literal_vector;
        typedef svector<euf::theory_var> vars;
        typedef std::pair<numeral, unsigned> value_sort_pair;
        typedef pair_hash<obj_hash<numeral>, unsigned_hash> value_sort_pair_hash;
        typedef map<value_sort_pair, theory_var, value_sort_pair_hash, default_eq<value_sort_pair> > value2var;
        typedef union_find<solver>  bv_union_find;
        typedef std::pair<theory_var, unsigned> var_pos;

        friend class ackerman;

        struct stats {
            unsigned   m_num_diseq_static, m_num_diseq_dynamic,  m_num_conflicts;
            unsigned   m_num_bit2eq, m_num_bit2ne, m_num_eq2bit, m_num_ne2bit;
            unsigned   m_ackerman;
            void reset() { memset(this, 0, sizeof(stats)); }
            stats() { reset(); }
        };

        struct bv_justification {
            enum kind_t { eq2bit, ne2bit, bit2eq, bit2ne, bv2int };
            kind_t     m_kind;
            unsigned   m_idx = UINT_MAX;
            theory_var m_v1 = euf::null_theory_var;
            theory_var m_v2 = euf::null_theory_var;
            sat::literal m_consequent;
            sat::literal m_antecedent;
            euf::enode* a, *b, *c;
                    
            bv_justification(theory_var v1, theory_var v2, sat::literal c, sat::literal a) :
                m_kind(bv_justification::kind_t::eq2bit), m_v1(v1), m_v2(v2), m_consequent(c), m_antecedent(a) {}
            bv_justification(theory_var v1, theory_var v2):
                m_kind(bv_justification::kind_t::bit2eq), m_v1(v1), m_v2(v2) {}
            bv_justification(unsigned idx, sat::literal c) :
                m_kind(bv_justification::kind_t::bit2ne), m_idx(idx), m_consequent(c) {}
            bv_justification(unsigned idx, theory_var v1, theory_var v2, sat::literal c, sat::literal a) :
                m_kind(bv_justification::kind_t::ne2bit), m_idx(idx), m_v1(v1), m_v2(v2), m_consequent(c), m_antecedent(a) {}
            bv_justification(theory_var v1, theory_var v2, euf::enode* a, euf::enode* b, euf::enode* c):
                m_kind(bv_justification::kind_t::bv2int), m_v1(v1), m_v2(v2), a(a), b(b), c(c) {}
            sat::ext_constraint_idx to_index() const { 
                return sat::constraint_base::mem2base(this); 
            }
            static bv_justification& from_index(size_t idx) {
                return *reinterpret_cast<bv_justification*>(sat::constraint_base::from_index(idx)->mem());
            }
            static size_t get_obj_size() { return sat::constraint_base::obj_size(sizeof(bv_justification)); }
        };

        sat::justification mk_eq2bit_justification(theory_var v1, theory_var v2, sat::literal c, sat::literal a);
        sat::ext_justification_idx mk_bit2eq_justification(theory_var v1, theory_var v2);
        sat::justification mk_bit2ne_justification(unsigned idx, sat::literal c);
        sat::justification mk_ne2bit_justification(unsigned idx, theory_var v1, theory_var v2, sat::literal c, sat::literal a);
        sat::ext_constraint_idx mk_bv2int_justification(theory_var v1, theory_var v2, euf::enode* a, euf::enode* b, euf::enode* c);
        void log_drat(bv_justification const& c);
        class proof_hint : public euf::th_proof_hint {
            bv_justification::kind_t   m_kind;
            sat::literal_vector& m_proof_literals;
            unsigned m_lit_head, m_lit_tail;
            expr* a1 = nullptr, * a2 = nullptr, * b1 = nullptr, * b2 = nullptr;
        public:
            proof_hint(bv_justification::kind_t k, sat::literal_vector& pl, unsigned lh, unsigned lt, expr* a1 = nullptr, expr* a2 = nullptr, expr* b1 = nullptr, expr* b2 = nullptr) :
                m_kind(k), m_proof_literals(pl), m_lit_head(lh), m_lit_tail(lt), a1(a1), a2(a2), b1(b1), b2(b2) {}
            expr* get_hint(euf::solver& s) const override;
        };
        sat::literal_vector m_proof_literals;
        unsigned m_lit_head = 0, m_lit_tail = 0;
 
        /**
           \brief Structure used to store the position of a bitvector variable that
           contains the true_literal/false_literal.
           
           Remark: the implementation assumes that bitvector variables containing
           complementary bits are never merged. I assert a disequality (not (= x y))
           whenever x and y contain complementary bits. However, this is too expensive
           when the bit is the true_literal or false_literal. The number of disequalities
           is too big. To avoid this problem, each equivalence class has a set
           of its true_literal and false_literal bits in the form of svector<zero_one_bit>.
           
           Before merging two classes we just check if the merge is valid by traversing these
           vectors.
        */
        struct zero_one_bit {
            theory_var m_owner;    //!< variable that owns the bit: useful for backtracking
            unsigned   m_idx:31;
            unsigned   m_is_true:1;
            zero_one_bit(theory_var v = euf::null_theory_var, unsigned idx = UINT_MAX, bool is_true = false):
                m_owner(v), m_idx(idx), m_is_true(is_true) {}
            std::ostream& display(std::ostream& out) const {
                return out << "v" << m_owner << " @ " << m_idx << " " << (m_is_true?"T":"F");
            }
        };

        typedef svector<zero_one_bit> zero_one_bits;

        struct eq_occurs {
            sat::bool_var m_bv1;
            sat::bool_var m_bv2;
            unsigned    m_idx;
            theory_var  m_v1;
            theory_var  m_v2;
            sat::literal m_literal;
            euf::enode* m_node;
            eq_occurs* m_next;
            eq_occurs* m_prev;
            eq_occurs(sat::bool_var b1, sat::bool_var b2, unsigned idx, theory_var v1, theory_var v2, sat::literal lit, euf::enode* n, eq_occurs* next = nullptr) :
                m_bv1(b1), m_bv2(b2), m_idx(idx), m_v1(v1), m_v2(v2), m_literal(lit), m_node(n), m_next(next), m_prev(nullptr) {}
        };

        class eq_occurs_it {
            eq_occurs* m_first;
        public:
            eq_occurs_it(eq_occurs* c) : m_first(c) {}
            eq_occurs const& operator*() { return *m_first; }
            eq_occurs_it& operator++() { m_first = m_first->m_next; return *this; }
            eq_occurs_it operator++(int) { eq_occurs_it tmp = *this; ++*this; return tmp; }
            bool operator==(eq_occurs_it const& other) const { return m_first == other.m_first; }
            bool operator!=(eq_occurs_it const& other) const { return !(*this == other); }
        };

        class eqs_iterator {
            eq_occurs* o;
        public:
            eqs_iterator(eq_occurs* o) :o(o) {}
            eq_occurs_it begin() const { return eq_occurs_it(o); }
            eq_occurs_it end() const { return eq_occurs_it(nullptr); }
        };

        struct var_pos_occ {
            var_pos       m_vp;
            var_pos_occ* m_next;
            var_pos_occ(theory_var v = euf::null_theory_var, unsigned idx = 0, var_pos_occ* next = nullptr) :m_vp(v, idx), m_next(next) {}
        };

        class var_pos_it {
            var_pos_occ* m_first;
        public:
            var_pos_it(var_pos_occ* c) : m_first(c) {}
            var_pos operator*() { return m_first->m_vp; }
            var_pos_it& operator++() { m_first = m_first->m_next; return *this; }
            var_pos_it operator++(int) { var_pos_it tmp = *this; ++* this; return tmp; }
            bool operator==(var_pos_it const& other) const { return m_first == other.m_first; }
            bool operator!=(var_pos_it const& other) const { return !(*this == other); }
        };

        struct atom {
            bool_var      m_bv;
            eq_occurs*    m_eqs = nullptr;
            var_pos_occ * m_occs = nullptr;
            svector<std::pair<atom*, eq_occurs*>> m_bit2occ;
            literal    m_var = sat::null_literal;
            literal    m_def = sat::null_literal;
            atom(bool_var b) :m_bv(b), m_eqs(nullptr), m_occs(nullptr) {}
            ~atom() { m_bit2occ.clear(); }
            var_pos_it begin() const { return var_pos_it(m_occs); }
            var_pos_it end() const { return var_pos_it(nullptr); }
            eqs_iterator eqs() const { return eqs_iterator(m_eqs); }  
        };

        struct propagation_item {
            var_pos m_vp = var_pos(0, 0) ;
            atom* m_atom = nullptr;
            explicit propagation_item(atom* a) : m_atom(a) {}
            explicit propagation_item(var_pos const& vp) : m_vp(vp) {}            
            bool operator==(propagation_item const& other) const { if (m_atom) return m_atom == other.m_atom; return false; }
            bool is_atom() const { return m_atom != nullptr; }
        };


        class bit_trail;
        class add_var_pos_trail;
        class add_eq_occurs_trail;
        class del_eq_occurs_trail;
        class mk_atom_trail;
        class bit_occs_trail;
        typedef ptr_vector<atom> bool_var2atom;
        typedef vector<literal_vector> bits_vector;

        bv_util                  bv;
        arith_util               m_autil;
        stats                    m_stats;
        ackerman                 m_ackerman;
        bit_blaster              m_bb;
        bv_union_find            m_find;
        bits_vector              m_bits;     // per var, the bits of a given variable.
        unsigned_vector          m_wpos;     // per var, watch position for fixed variable detection. 
        vector<zero_one_bits>    m_zero_one_bits; // per var, see comment in the struct zero_one_bit
        bool_var2atom            m_bool_var2atom;
        value2var                m_fixed_var_table;
        mutable vector<rational>   m_power2;
        literal_vector             m_tmp_literals;
        svector<propagation_item>  m_prop_queue;
        unsigned_vector            m_prop_queue_lim;
        unsigned                   m_prop_queue_head = 0;
        sat::literal               m_true = sat::null_literal;
        euf::enode_vector          m_bv2ints;
        obj_map<app, lazy_mul*>   m_lazymul;

        // internalize
        void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); }
        void erase_bv2a(bool_var bv) { m_bool_var2atom[bv] = 0; }
        atom * get_bv2a(bool_var bv) const { return m_bool_var2atom.get(bv, 0); }
        bool visit(expr* e) override;
        bool visited(expr* e) override;
        bool post_visit(expr* e, bool sign, bool root) override;
        unsigned get_bv_size(euf::enode* n);
        unsigned get_bv_size(theory_var v);
        theory_var get_var(euf::enode* n);
        euf::enode* get_arg(euf::enode* n, unsigned idx);
        inline theory_var get_arg_var(euf::enode* n, unsigned idx);
        void get_bits(theory_var v, expr_ref_vector& r);
        void get_bits(euf::enode* n, expr_ref_vector& r);
        void get_arg_bits(app* n, unsigned idx, expr_ref_vector& r);
        void fixed_var_eh(theory_var v);
        bool is_fixed(euf::theory_var v, expr_ref& val, sat::literal_vector& lits) override;
        bool is_bv(theory_var v) const { return bv.is_bv(var2expr(v)); }
        void register_true_false_bit(theory_var v, unsigned i);
        void add_bit(theory_var v, sat::literal lit);
        atom* mk_atom(sat::bool_var b);
        void eq_internalized(sat::bool_var b1, sat::bool_var b2, unsigned idx, theory_var v1, theory_var v2, sat::literal eq, euf::enode* n);
        void del_eq_occurs(atom* a, eq_occurs* occ);

        void set_bit_eh(theory_var v, literal l, unsigned idx);
        void init_bits(expr* e, expr_ref_vector const & bits);
        void mk_bits(theory_var v);
        void add_def(sat::literal def, sat::literal l);
        bool internalize_circuit(app* a);
        void internalize_unary(app* n, std::function<void(unsigned, expr* const*, expr_ref_vector&)>& fn);
        void internalize_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn);
        void internalize_par_unary(app* n, std::function<void(unsigned, expr* const*, unsigned p, expr_ref_vector&)>& fn);
        void internalize_novfl(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref&)>& fn);
        void internalize_interp(app* n, std::function<expr*(expr*, expr*)>& ibin, std::function<expr*(expr*)>& un);
        void internalize_num(app * n);       
        void internalize_concat(app * n);        
        void internalize_bv2int(app* n);
        void internalize_int2bv(app* n);
        void internalize_mkbv(app* n);
        void internalize_xor3(app* n);
        void internalize_carry(app* n);
        void internalize_sub(app* n);
        void internalize_extract(app* n);
        void internalize_repeat(app* n);
        void internalize_bit2bool(app* n);
        void internalize_overflow(app* n);
        void internalize_udiv_i(app* n);
        void assert_bv_divrem_bound_axiom(app* n);
        template<bool Signed, bool Reverse, bool Negated>
        void internalize_le(app* n);
        void assert_bv2int_axiom(app * n);
        void assert_int2bv_axiom(app* n);
        void assert_ackerman(theory_var v1, theory_var v2);
        bool reflect() const { return get_config().m_bv_reflect; }

        // delay internalize
        enum class internalize_mode {
            delay_i,
            no_delay_i,
            init_bits_only_i
        };

        obj_map<expr, internalize_mode> m_delay_internalize;
        bool m_cheap_axioms{ true };
        bool should_bit_blast(app * n);
        bool check_delay_internalized(expr* e);
        bool check_lazy_mul(app* e, expr* mul_value, expr* arg_value);
        bool check_mul(app* e);
        bool check_mul_invertibility(app* n, expr_ref_vector const& arg_values, expr* value);
        bool check_mul_zero(app* n, expr_ref_vector const& arg_values, expr* value1, expr* value2);
        bool check_mul_one(app* n, expr_ref_vector const& arg_values, expr* value1, expr* value2);
        bool check_umul_no_overflow(app* n, expr_ref_vector const& arg_values, expr* value);
        bool check_bv_eval(euf::enode* n);
        bool check_bool_eval(euf::enode* n);
        void encode_msb_tail(expr* x, expr_ref_vector& xs);
        void encode_lsb_tail(expr* x, expr_ref_vector& xs);
        internalize_mode get_internalize_mode(expr* e);
        void set_delay_internalize(expr* e, internalize_mode mode);
        expr_ref eval_args(euf::enode* n, expr_ref_vector& eargs);
        expr_ref eval_bv(euf::enode* n);
        
        // solving
        theory_var find(theory_var v) const { return m_find.find(v); }
        bool find_wpos(theory_var v);
        void find_new_diseq_axioms(atom& a, theory_var v, unsigned idx);
        void mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx);
        bool get_fixed_value(theory_var v, numeral& result) const;
        void add_fixed_eq(theory_var v1, theory_var v2);      
        svector<theory_var>   m_merge_aux[2]; //!< auxiliary vector used in merge_zero_one_bits
        bool merge_zero_one_bits(theory_var r1, theory_var r2);
        bool assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc);
        bool propagate_bits(var_pos entry);
        bool propagate_eq_occurs(eq_occurs const& occ);
        numeral const& power2(unsigned i) const;
        sat::literal mk_true();

        // invariants
        bool check_zero_one_bits(theory_var v);
        void check_missing_propagation() const;
        void validate_atoms() const;
        
        std::ostream& display(std::ostream& out, atom const& a) const;
       
    public:
        solver(euf::solver& ctx, theory_id id);
        void set_lookahead(sat::lookahead* s) override { }
        void init_search() override {}
        double get_reward(literal l, sat::ext_constraint_idx idx, sat::literal_occs_fun& occs) const override;
        bool is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) override;
        bool is_external(bool_var v) override;
        void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r, bool probing) override;
        euf::enode_pair get_justification_eq(size_t j) override;
        void asserted(literal l) override;
        sat::check_result check() override;
        void push_core() override;
        void pop_core(unsigned n) override;   
        void simplify() override;
        bool set_root(literal l, literal r) override;
        void flush_roots() override;
        void clauses_modifed() override;
        lbool get_phase(bool_var v) override;
        std::ostream& display(std::ostream& out) const override;
        std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override;
        std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;
        void collect_statistics(statistics& st) const override;
        euf::th_solver* clone(euf::solver& ctx) override;
        extension* copy(sat::solver* s) override;       
        void find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) override {}
        void gc() override {}
        void pop_reinit() override;
        bool validate() override;
        void init_use_list(sat::ext_use_list& ul) override;
        bool is_blocked(literal l, sat::ext_constraint_idx) override;
        bool check_model(sat::model const& m) const override;
        void finalize_model(model& mdl) override;

        void new_eq_eh(euf::th_eq const& eq) override;
        void new_diseq_eh(euf::th_eq const& ne) override;
        bool use_diseqs() const override { return true; }
        bool unit_propagate() override;

        void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;

        bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card,
                        std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override { return false; }

        bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override { return false; }
        sat::literal internalize(expr* e, bool sign, bool root) override;
        void internalize(expr* e) override;
        void eq_internalized(euf::enode* n) override;
        euf::theory_var mk_var(euf::enode* n) override;
        void apply_sort_cnstr(euf::enode * n, sort * s) override;

        bool_var get_bit(unsigned bit, euf::enode* n) const;
        
        void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2);
        void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { SASSERT(check_zero_one_bits(r1)); }
        void unmerge_eh(theory_var v1, theory_var v2);
        trail_stack& get_trail_stack();

        // diagnostics
        std::ostream& display(std::ostream& out, theory_var v) const;        
        typedef std::pair<solver const*, theory_var> pp_var;
        pp_var pp(theory_var v) const { return pp_var(this, v); }

        friend std::ostream& operator<<(std::ostream& out, solver::zero_one_bit const& zo) { return zo.display(out); }

    };

    inline std::ostream& operator<<(std::ostream& out, solver::pp_var const& p) { return p.first->display(out, p.second); }



}