Skip to content

Commit 9293923

Browse files
committedDec 15, 2023
Add intblast solver
1 parent 0520558 commit 9293923

28 files changed

+1621
-58
lines changed
 

Diff for: ‎RELEASE_NOTES.md

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ Version 4.next
1212

1313
Version 4.12.5
1414
==============
15+
- Fixes to pypi setup and build for MacOS distributions
16+
- A new theory solver "int-blast" enabled by using:
17+
- sat.smt=true smt.bv.solver=2
18+
- It solves a few bit-vector problems not handled by bit-blasting, especially if the bit-widths are large.
19+
- It is based on encoding bit-vector constraints to non-linear integer arithemtic.
20+
1521

1622
Version 4.12.4
1723
==============

Diff for: ‎src/ast/arith_decl_plugin.cpp

+27-4
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,12 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
523523
return m_manager->mk_func_decl(symbol("divisible"), 1, &m_int_decl, m_manager->mk_bool_sort(),
524524
func_decl_info(m_family_id, k, num_parameters, parameters));
525525
}
526+
if (k == OP_ARITH_BAND) {
527+
if (arity != 2 || domain[0] != m_int_decl || domain[1] != m_int_decl || num_parameters != 1 || !parameters[0].is_int())
528+
m_manager->raise_exception("invalid bitwise and application. Expects integer parameter and two arguments of sort integer");
529+
return m_manager->mk_func_decl(symbol("band"), 2, domain, m_int_decl,
530+
func_decl_info(m_family_id, k, num_parameters, parameters));
531+
}
526532

527533
if (m_manager->int_real_coercions() && use_coercion(k)) {
528534
return mk_func_decl(fix_kind(k, arity), has_real_arg(arity, domain, m_real_decl));
@@ -548,6 +554,14 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
548554
return m_manager->mk_func_decl(symbol("divisible"), 1, &m_int_decl, m_manager->mk_bool_sort(),
549555
func_decl_info(m_family_id, k, num_parameters, parameters));
550556
}
557+
if (k == OP_ARITH_BAND) {
558+
if (num_args != 2 || args[0]->get_sort() != m_int_decl || args[1]->get_sort() != m_int_decl || num_parameters != 1 || !parameters[0].is_int())
559+
m_manager->raise_exception("invalid bitwise and application. Expects integer parameter and two arguments of sort integer");
560+
sort* domain[2] = { m_int_decl, m_int_decl };
561+
return m_manager->mk_func_decl(symbol("band"), 2, domain, m_int_decl,
562+
func_decl_info(m_family_id, k, num_parameters, parameters));
563+
}
564+
551565
if (m_manager->int_real_coercions() && use_coercion(k)) {
552566
return mk_func_decl(fix_kind(k, num_args), has_real_arg(m_manager, num_args, args, m_real_decl));
553567
}
@@ -693,7 +707,16 @@ expr * arith_decl_plugin::get_some_value(sort * s) {
693707
return mk_numeral(rational(0), s == m_int_decl);
694708
}
695709

696-
bool arith_recognizers::is_numeral(expr const * n, rational & val, bool & is_int) const {
710+
bool arith_util::is_numeral(expr const * n, rational & val, bool & is_int) const {
711+
if (is_irrational_algebraic_numeral(n)) {
712+
scoped_anum an(am());
713+
is_irrational_algebraic_numeral2(n, an);
714+
if (am().is_rational(an)) {
715+
am().to_rational(an, val);
716+
is_int = val.is_int();
717+
return true;
718+
}
719+
}
697720
if (!is_app_of(n, arith_family_id, OP_NUM))
698721
return false;
699722
func_decl * decl = to_app(n)->get_decl();
@@ -724,7 +747,7 @@ bool arith_recognizers::is_int_expr(expr const *e) const {
724747
if (is_to_real(e)) {
725748
// pass
726749
}
727-
else if (is_numeral(e, r) && r.is_int()) {
750+
else if (is_numeral(e) && is_int(e)) {
728751
// pass
729752
}
730753
else if (is_add(e) || is_mul(e)) {
@@ -747,14 +770,14 @@ void arith_util::init_plugin() {
747770
m_plugin = static_cast<arith_decl_plugin*>(m_manager.get_plugin(arith_family_id));
748771
}
749772

750-
bool arith_util::is_irrational_algebraic_numeral2(expr const * n, algebraic_numbers::anum & val) {
773+
bool arith_util::is_irrational_algebraic_numeral2(expr const * n, algebraic_numbers::anum & val) const {
751774
if (!is_app_of(n, arith_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM))
752775
return false;
753776
am().set(val, to_irrational_algebraic_numeral(n));
754777
return true;
755778
}
756779

757-
algebraic_numbers::anum const & arith_util::to_irrational_algebraic_numeral(expr const * n) {
780+
algebraic_numbers::anum const & arith_util::to_irrational_algebraic_numeral(expr const * n) const {
758781
SASSERT(is_irrational_algebraic_numeral(n));
759782
return plugin().aw().to_anum(to_app(n)->get_decl());
760783
}

Diff for: ‎src/ast/arith_decl_plugin.h

+41-24
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ enum arith_op_kind {
7070
OP_ASINH,
7171
OP_ACOSH,
7272
OP_ATANH,
73+
// Bit-vector functions
74+
OP_ARITH_BAND,
7375
// constants
7476
OP_PI,
7577
OP_E,
@@ -235,26 +237,10 @@ class arith_recognizers {
235237
family_id get_family_id() const { return arith_family_id; }
236238

237239
bool is_arith_expr(expr const * n) const { return is_app(n) && to_app(n)->get_family_id() == arith_family_id; }
238-
bool is_irrational_algebraic_numeral(expr const * n) const;
239-
bool is_unsigned(expr const * n, unsigned& u) const {
240-
rational val;
241-
bool is_int = true;
242-
return is_numeral(n, val, is_int) && is_int && val.is_unsigned() && (u = val.get_unsigned(), true);
243-
}
244-
bool is_numeral(expr const * n, rational & val, bool & is_int) const;
245-
bool is_numeral(expr const * n, rational & val) const { bool is_int; return is_numeral(n, val, is_int); }
246-
bool is_numeral(expr const * n) const { return is_app_of(n, arith_family_id, OP_NUM); }
247-
bool is_zero(expr const * n) const { rational val; return is_numeral(n, val) && val.is_zero(); }
248-
bool is_minus_one(expr * n) const { rational tmp; return is_numeral(n, tmp) && tmp.is_minus_one(); }
249-
// return true if \c n is a term of the form (* -1 r)
250-
bool is_times_minus_one(expr * n, expr * & r) const {
251-
if (is_mul(n) && to_app(n)->get_num_args() == 2 && is_minus_one(to_app(n)->get_arg(0))) {
252-
r = to_app(n)->get_arg(1);
253-
return true;
254-
}
255-
return false;
256-
}
257240

241+
bool is_irrational_algebraic_numeral(expr const* n) const;
242+
243+
bool is_numeral(expr const* n) const { return is_app_of(n, arith_family_id, OP_NUM); }
258244
bool is_int_expr(expr const * e) const;
259245

260246
bool is_le(expr const * n) const { return is_app_of(n, arith_family_id, OP_LE); }
@@ -309,6 +295,16 @@ class arith_recognizers {
309295
bool is_int_real(sort const * s) const { return s->get_family_id() == arith_family_id; }
310296
bool is_int_real(expr const * n) const { return is_int_real(n->get_sort()); }
311297

298+
bool is_band(expr const* n) const { return is_app_of(n, arith_family_id, OP_ARITH_BAND); }
299+
bool is_band(expr const* n, unsigned& sz, expr*& x, expr*& y) {
300+
if (!is_band(n))
301+
return false;
302+
x = to_app(n)->get_arg(0);
303+
y = to_app(n)->get_arg(1);
304+
sz = to_app(n)->get_parameter(0).get_int();
305+
return true;
306+
}
307+
312308
bool is_sin(expr const* n) const { return is_app_of(n, arith_family_id, OP_SIN); }
313309
bool is_cos(expr const* n) const { return is_app_of(n, arith_family_id, OP_COS); }
314310
bool is_tan(expr const* n) const { return is_app_of(n, arith_family_id, OP_TAN); }
@@ -387,13 +383,32 @@ class arith_util : public arith_recognizers {
387383
return *m_plugin;
388384
}
389385

390-
algebraic_numbers::manager & am() {
386+
algebraic_numbers::manager & am() const {
391387
return plugin().am();
392388
}
393389

390+
// return true if \c n is a term of the form (* -1 r)
391+
bool is_zero(expr const* n) const { rational val; return is_numeral(n, val) && val.is_zero(); }
392+
bool is_minus_one(expr* n) const { rational tmp; return is_numeral(n, tmp) && tmp.is_minus_one(); }
393+
bool is_times_minus_one(expr* n, expr*& r) const {
394+
if (is_mul(n) && to_app(n)->get_num_args() == 2 && is_minus_one(to_app(n)->get_arg(0))) {
395+
r = to_app(n)->get_arg(1);
396+
return true;
397+
}
398+
return false;
399+
}
400+
bool is_unsigned(expr const* n, unsigned& u) const {
401+
rational val;
402+
bool is_int = true;
403+
return is_numeral(n, val, is_int) && is_int && val.is_unsigned() && (u = val.get_unsigned(), true);
404+
}
405+
bool is_numeral(expr const* n) const { return arith_recognizers::is_numeral(n); }
406+
bool is_numeral(expr const* n, rational& val, bool& is_int) const;
407+
bool is_numeral(expr const* n, rational& val) const { bool is_int; return is_numeral(n, val, is_int); }
408+
394409
bool convert_int_numerals_to_real() const { return plugin().convert_int_numerals_to_real(); }
395-
bool is_irrational_algebraic_numeral2(expr const * n, algebraic_numbers::anum & val);
396-
algebraic_numbers::anum const & to_irrational_algebraic_numeral(expr const * n);
410+
bool is_irrational_algebraic_numeral2(expr const * n, algebraic_numbers::anum & val) const;
411+
algebraic_numbers::anum const & to_irrational_algebraic_numeral(expr const * n) const;
397412

398413
sort * mk_int() { return m_manager.mk_sort(arith_family_id, INT_SORT); }
399414
sort * mk_real() { return m_manager.mk_sort(arith_family_id, REAL_SORT); }
@@ -471,6 +486,8 @@ class arith_util : public arith_recognizers {
471486
app * mk_power(expr* arg1, expr* arg2) { return m_manager.mk_app(arith_family_id, OP_POWER, arg1, arg2); }
472487
app * mk_power0(expr* arg1, expr* arg2) { return m_manager.mk_app(arith_family_id, OP_POWER0, arg1, arg2); }
473488

489+
app* mk_band(unsigned n, expr* arg1, expr* arg2) { parameter p(n); expr* args[2] = { arg1, arg2 }; return m_manager.mk_app(arith_family_id, OP_ARITH_BAND, 1, &p, 2, args); }
490+
474491
app * mk_sin(expr * arg) { return m_manager.mk_app(arith_family_id, OP_SIN, arg); }
475492
app * mk_cos(expr * arg) { return m_manager.mk_app(arith_family_id, OP_COS, arg); }
476493
app * mk_tan(expr * arg) { return m_manager.mk_app(arith_family_id, OP_TAN, arg); }
@@ -498,11 +515,11 @@ class arith_util : public arith_recognizers {
498515
if none of them are numerals, then the left-hand-side has a smaller id than the right hand side.
499516
*/
500517
app * mk_eq(expr * lhs, expr * rhs) {
501-
if (is_numeral(lhs) || (!is_numeral(rhs) && lhs->get_id() > rhs->get_id()))
518+
if (arith_recognizers::is_numeral(lhs) || (!arith_recognizers::is_numeral(rhs) && lhs->get_id() > rhs->get_id()))
502519
std::swap(lhs, rhs);
503520
if (lhs == rhs)
504521
return m_manager.mk_true();
505-
if (is_numeral(lhs) && is_numeral(rhs)) {
522+
if (arith_recognizers::is_numeral(lhs) && arith_recognizers::is_numeral(rhs)) {
506523
SASSERT(lhs != rhs);
507524
return m_manager.mk_false();
508525
}

Diff for: ‎src/ast/bv_decl_plugin.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -942,3 +942,8 @@ app * bv_util::mk_bv2int(expr* e) {
942942
parameter p(s);
943943
return m_manager.mk_app(get_fid(), OP_BV2INT, 1, &p, 1, &e);
944944
}
945+
946+
app* bv_util::mk_int2bv(unsigned sz, expr* e) {
947+
parameter p(sz);
948+
return m_manager.mk_app(get_fid(), OP_INT2BV, 1, &p, 1, &e);
949+
}

Diff for: ‎src/ast/bv_decl_plugin.h

+29
Original file line numberDiff line numberDiff line change
@@ -386,9 +386,31 @@ class bv_recognizers {
386386
bool is_bv_shl(expr const * e) const { return is_app_of(e, get_fid(), OP_BSHL); }
387387
bool is_sign_ext(expr const * e) const { return is_app_of(e, get_fid(), OP_SIGN_EXT); }
388388
bool is_bv_umul_no_ovfl(expr const* e) const { return is_app_of(e, get_fid(), OP_BUMUL_NO_OVFL); }
389+
bool is_redand(expr const* e) const { return is_app_of(e, get_fid(), OP_BREDAND); }
390+
bool is_redor(expr const* e) const { return is_app_of(e, get_fid(), OP_BREDOR); }
391+
bool is_comp(expr const* e) const { return is_app_of(e, get_fid(), OP_BCOMP); }
392+
bool is_rotate_left(expr const* e) const { return is_app_of(e, get_fid(), OP_ROTATE_LEFT); }
393+
bool is_rotate_right(expr const* e) const { return is_app_of(e, get_fid(), OP_ROTATE_RIGHT); }
394+
bool is_ext_rotate_left(expr const* e) const { return is_app_of(e, get_fid(), OP_EXT_ROTATE_LEFT); }
395+
bool is_ext_rotate_right(expr const* e) const { return is_app_of(e, get_fid(), OP_EXT_ROTATE_RIGHT); }
396+
397+
bool is_rotate_left(expr const* e, unsigned& n, expr*& x) const {
398+
return is_rotate_left(e) && (n = to_app(e)->get_parameter(0).get_int(), x = to_app(e)->get_arg(0), true);
399+
}
400+
bool is_rotate_right(expr const* e, unsigned& n, expr*& x) const {
401+
return is_rotate_right(e) && (n = to_app(e)->get_parameter(0).get_int(), x = to_app(e)->get_arg(0), true);
402+
}
403+
bool is_int2bv(expr const* e, unsigned& n, expr*& x) const {
404+
return is_int2bv(e) && (n = to_app(e)->get_parameter(0).get_int(), x = to_app(e)->get_arg(0), true);
405+
}
389406

390407
MATCH_UNARY(is_bv_not);
408+
MATCH_UNARY(is_redand);
409+
MATCH_UNARY(is_redor);
391410

411+
MATCH_BINARY(is_ext_rotate_left);
412+
MATCH_BINARY(is_ext_rotate_right);
413+
MATCH_BINARY(is_comp);
392414
MATCH_BINARY(is_bv_add);
393415
MATCH_BINARY(is_bv_sub);
394416
MATCH_BINARY(is_bv_mul);
@@ -411,6 +433,12 @@ class bv_recognizers {
411433
MATCH_BINARY(is_bv_sdiv);
412434
MATCH_BINARY(is_bv_udiv);
413435
MATCH_BINARY(is_bv_smod);
436+
MATCH_BINARY(is_bv_and);
437+
MATCH_BINARY(is_bv_or);
438+
MATCH_BINARY(is_bv_xor);
439+
MATCH_BINARY(is_bv_nand);
440+
MATCH_BINARY(is_bv_nor);
441+
414442

415443
MATCH_BINARY(is_bv_uremi);
416444
MATCH_BINARY(is_bv_sremi);
@@ -516,6 +544,7 @@ class bv_util : public bv_recognizers {
516544
app * mk_bv_lshr(expr* arg1, expr* arg2) { return m_manager.mk_app(get_fid(), OP_BLSHR, arg1, arg2); }
517545

518546
app * mk_bv2int(expr* e);
547+
app * mk_int2bv(unsigned sz, expr* e);
519548

520549
// TODO: all these binary ops commute (right?) but it'd be more logical to swap `n` & `m` in the `return`
521550
app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); }

Diff for: ‎src/ast/euf/euf_bv_plugin.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ namespace euf {
104104
}
105105

106106
void bv_plugin::merge_eh(enode* x, enode* y) {
107-
SASSERT(x == x->get_root());
108-
SASSERT(x == y->get_root());
107+
if (!bv.is_bv(x->get_expr()))
108+
return;
109109

110110
TRACE("bv", tout << "merge_eh " << g.bpp(x) << " == " << g.bpp(y) << "\n");
111111
SASSERT(!m_internal);
@@ -343,7 +343,8 @@ namespace euf {
343343
enode* hi = mk_extract(n, cut, w - 1);
344344
enode* lo = mk_extract(n, 0, cut - 1);
345345
auto& i = info(n);
346-
SASSERT(i.value);
346+
if (!i.value)
347+
i.value = n;
347348
i.hi = hi;
348349
i.lo = lo;
349350
i.cut = cut;

Diff for: ‎src/math/dd/dd_pdd.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1807,7 +1807,9 @@ namespace dd {
18071807
pdd& pdd::operator=(pdd const& other) {
18081808
if (m != other.m) {
18091809
verbose_stream() << "pdd manager confusion: " << *this << " (mod 2^" << power_of_2() << ") := " << other << " (mod 2^" << other.power_of_2() << ")\n";
1810+
UNREACHABLE();
18101811
// TODO: in the end, this operator should probably be changed to also update the manager. But for now I want to detect such confusions.
1812+
reset(*other.m);
18111813
}
18121814
SASSERT_EQ(power_of_2(), other.power_of_2());
18131815
VERIFY_EQ(power_of_2(), other.power_of_2());

Diff for: ‎src/math/lp/lp_api.h

+2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ namespace lp_api {
108108
unsigned m_gomory_cuts;
109109
unsigned m_assume_eqs;
110110
unsigned m_branch;
111+
unsigned m_band_axioms;
111112
stats() { reset(); }
112113
void reset() {
113114
memset(this, 0, sizeof(*this));
@@ -128,6 +129,7 @@ namespace lp_api {
128129
st.update("arith-gomory-cuts", m_gomory_cuts);
129130
st.update("arith-assume-eqs", m_assume_eqs);
130131
st.update("arith-branch", m_branch);
132+
st.update("arith-band-axioms", m_band_axioms);
131133
}
132134
};
133135

Diff for: ‎src/sat/sat_solver.cpp

+13-8
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,6 @@ namespace sat {
879879
m_conflict = c;
880880
m_not_l = not_l;
881881
TRACE("sat", display(display_justification(tout << "conflict " << not_l << " ", c) << "\n"));
882-
TRACE("sat", display_watches(tout));
883882
}
884883

885884
void solver::assign_core(literal l, justification j) {
@@ -1720,6 +1719,9 @@ namespace sat {
17201719
if (next == null_bool_var)
17211720
return false;
17221721
}
1722+
else {
1723+
SASSERT(value(next) == l_undef);
1724+
}
17231725
push();
17241726
m_stats.m_decision++;
17251727

@@ -1729,11 +1731,14 @@ namespace sat {
17291731
phase = guess(next) ? l_true: l_false;
17301732

17311733
literal next_lit(next, false);
1734+
SASSERT(value(next_lit) == l_undef);
17321735

17331736
if (m_ext && m_ext->decide(next, phase)) {
1737+
17341738
if (used_queue)
17351739
m_case_split_queue.unassign_var_eh(next);
17361740
next_lit = literal(next, false);
1741+
SASSERT(value(next_lit) == l_undef);
17371742
}
17381743

17391744
if (phase == l_undef)
@@ -2429,9 +2434,8 @@ namespace sat {
24292434
m_conflicts_since_restart++;
24302435
m_conflicts_since_gc++;
24312436
m_stats.m_conflict++;
2432-
if (m_step_size > m_config.m_step_size_min) {
2433-
m_step_size -= m_config.m_step_size_dec;
2434-
}
2437+
if (m_step_size > m_config.m_step_size_min)
2438+
m_step_size -= m_config.m_step_size_dec;
24352439

24362440
bool unique_max;
24372441
m_conflict_lvl = get_max_lvl(m_not_l, m_conflict, unique_max);
@@ -2554,7 +2558,8 @@ namespace sat {
25542558
}
25552559
SASSERT(lvl(c_var) < m_conflict_lvl);
25562560
}
2557-
CTRACE("sat", idx == 0,
2561+
CTRACE("sat", idx == 0,
2562+
tout << "conflict level " << m_conflict_lvl << "\n";
25582563
for (literal lit : m_trail)
25592564
if (is_marked(lit.var()))
25602565
tout << "missed " << lit << "@" << lvl(lit) << "\n";);
@@ -2809,8 +2814,9 @@ namespace sat {
28092814
unsigned level = 0;
28102815

28112816
if (not_l != null_literal) {
2812-
level = lvl(not_l);
2817+
level = lvl(not_l);
28132818
}
2819+
TRACE("sat", tout << "level " << not_l << " is " << level << " " << js << "\n");
28142820

28152821
switch (js.get_kind()) {
28162822
case justification::NONE:
@@ -3485,11 +3491,10 @@ namespace sat {
34853491
//
34863492
// -----------------------
34873493
void solver::push() {
3494+
SASSERT(!m_ext || !m_ext->can_propagate());
34883495
SASSERT(!inconsistent());
34893496
TRACE("sat_verbose", tout << "q:" << m_qhead << " trail: " << m_trail.size() << "\n";);
34903497
SASSERT(m_qhead == m_trail.size());
3491-
if (m_ext)
3492-
m_ext->unit_propagate();
34933498
m_scopes.push_back(scope());
34943499
scope & s = m_scopes.back();
34953500
m_scope_lvl++;

0 commit comments

Comments
 (0)
Please sign in to comment.