3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-06 17:44:08 +00:00

move branch of unit variable

This commit is contained in:
Nikolaj Bjorner 2021-03-08 10:09:04 -08:00
parent 3c26a965e1
commit 7eceeff349
4 changed files with 238 additions and 238 deletions

View file

@ -8,9 +8,12 @@
from z3 import * from z3 import *
import random import random
counter = 0
def add_def(s, fml): def add_def(s, fml):
name = Bool(f"f{fml}") global counter
name = Bool(f"def-{counter}")
counter += 1
s.add(name == fml) s.add(name == fml)
return name return name
@ -52,7 +55,8 @@ def count_sets_by_size(sets):
class Soft: class Soft:
def __init__(self, soft): def __init__(self, soft):
self.formulas = soft self.formulas = set(soft)
self.original_soft = soft.copy()
self.offset = 0 self.offset = 0
self.init_names() self.init_names()
@ -60,6 +64,11 @@ class Soft:
self.name2formula = { Bool(f"s{s}") : s for s in self.formulas } self.name2formula = { Bool(f"s{s}") : s for s in self.formulas }
self.formula2name = { s : v for (v, s) in self.name2formula.items() } self.formula2name = { s : v for (v, s) in self.name2formula.items() }
#
# TODO: try to replace this by a recursive invocation of HsMaxSAT
# such that the invocation is incremental with respect to adding constraints
# and has resource bounded invocation.
#
class HsPicker: class HsPicker:
def __init__(self, soft): def __init__(self, soft):
self.soft = soft self.soft = soft
@ -75,7 +84,7 @@ class HsPicker:
hs = hs | { h } hs = hs | { h }
print("approximate hitting set", len(hs), "smallest possible size", lo) print("approximate hitting set", len(hs), "smallest possible size", lo)
return hs, lo return hs, lo
# #
# This can improve lower bound, but is expensive. # This can improve lower bound, but is expensive.
# Note that Z3 does not work well for hitting set optimization. # Note that Z3 does not work well for hitting set optimization.
@ -86,6 +95,8 @@ class HsPicker:
# #
def pick_hs(self, Ks, lo): def pick_hs(self, Ks, lo):
if len(Ks) == 0:
return set(), lo
if self.opt_backoff_count < self.opt_backoff_limit: if self.opt_backoff_count < self.opt_backoff_limit:
self.opt_backoff_count += 1 self.opt_backoff_count += 1
return self.pick_hs_(Ks, lo) return self.pick_hs_(Ks, lo)
@ -100,7 +111,7 @@ class HsPicker:
if is_sat == sat: if is_sat == sat:
mdl = opt.model() mdl = opt.model()
hs = [self.soft.name2formula[n] for n in self.soft.formula2name.values() if is_true(mdl.eval(n))] hs = [self.soft.name2formula[n] for n in self.soft.formula2name.values() if is_true(mdl.eval(n))]
return hs, lo return set(hs), lo
else: else:
print("Timeout", self.timeout_value, "lo", lo, "limit", self.opt_backoff_limit) print("Timeout", self.timeout_value, "lo", lo, "limit", self.opt_backoff_limit)
self.opt_backoff_limit += 1 self.opt_backoff_limit += 1
@ -113,17 +124,18 @@ class HsMaxSAT:
def __init__(self, soft, s): def __init__(self, soft, s):
self.s = s # solver object self.s = s # solver object
self.original_soft = soft
self.soft = Soft(soft) # Soft constraints self.soft = Soft(soft) # Soft constraints
self.hs = HsPicker(self.soft) # Pick a hitting set self.hs = HsPicker(self.soft) # Pick a hitting set
self.mdl = None # Current best model self.model = None # Current best model
self.lo = 0 # Current lower bound self.lo = 0 # Current lower bound
self.hi = len(soft) # Current upper bound self.hi = len(soft) # Current upper bound
self.Ks = [] # Set of Cores self.Ks = [] # Set of Cores
self.Cs = [] # Set of correction sets self.Cs = [] # Set of correction sets
self.small_set_size = 6 self.small_set_size = 6
self.small_set_threshold = 2 self.small_set_threshold = 1
self.num_max_res_failures = 0 self.num_max_res_failures = 0
self.corr_set_enabled = True
self.patterns = []
def has_many_small_sets(self, sets): def has_many_small_sets(self, sets):
small_count = len([c for c in sets if len(c) <= self.small_set_size]) small_count = len([c for c in sets if len(c) <= self.small_set_size])
@ -149,7 +161,6 @@ class HsMaxSAT:
self.Ks = [] self.Ks = []
self.Cs = [] self.Cs = []
self.lo -= num_cores_relaxed self.lo -= num_cores_relaxed
self.hi -= num_cores_relaxed
print("New offset", self.soft.offset) print("New offset", self.soft.offset)
def maxres(self): def maxres(self):
@ -160,26 +171,33 @@ class HsMaxSAT:
if self.has_many_small_sets(self.Ks): if self.has_many_small_sets(self.Ks):
self.num_max_res_failures = 0 self.num_max_res_failures = 0
cores = self.get_small_disjoint_sets(self.Ks) cores = self.get_small_disjoint_sets(self.Ks)
self.soft.formulas = set(self.soft.formulas)
for core in cores: for core in cores:
self.small_set_size = min(self.small_set_size, len(core) - 2) self.small_set_size = max(4, min(self.small_set_size, len(core) - 2))
relax_core(self.s, core, self.soft.formulas) relax_core(self.s, core, self.soft.formulas)
self.reinit_soft(len(cores)) self.reinit_soft(len(cores))
self.corr_set_enabled = True
return return
# #
# If there are sufficiently many small correction sets, then # If there are sufficiently many small correction sets, then
# we reduce the soft constraints by dual maxres (IJCAI 2014) # we reduce the soft constraints by dual maxres (IJCAI 2014)
#
# TODO: the heuristic for when to invoking correction set restriction
# needs fine-tuning. For example, the if min(Ks)*optimality_gap < min(Cs)*(max(SS))
# we might want to prioritize core relaxation to make progress with less overhead.
# here: max(SS) = |Soft|-min(Cs) is the size of the maximal satisfying subset
# the optimality gap is self.hi - self.offset
# which is a bound on how many cores have to be relaxed before determining optimality.
# #
if self.has_many_small_sets(self.Cs): if self.corr_set_enabled and self.has_many_small_sets(self.Cs):
self.num_max_res_failures = 0 self.num_max_res_failures = 0
cs = self.get_small_disjoint_sets(self.Cs) cs = self.get_small_disjoint_sets(self.Cs)
self.soft.formulas = set(self.soft.formulas)
for corr_set in cs: for corr_set in cs:
print("restrict cs", len(corr_set)) print("restrict cs", len(corr_set))
self.small_set_size = min(self.small_set_size, len(corr_set) - 2) self.small_set_size = max(4, min(self.small_set_size, len(corr_set) - 2))
restrict_cs(self.s, corr_set, self.soft.formulas) restrict_cs(self.s, corr_set, self.soft.formulas)
s.add(Or(corr_set)) self.s.add(Or(corr_set))
self.reinit_soft(0) self.reinit_soft(0)
self.corr_set_enabled = False
return return
# #
# Increment the failure count. If the failure count reaches a threshold # Increment the failure count. If the failure count reaches a threshold
@ -197,34 +215,55 @@ class HsMaxSAT:
def save_model(self): def save_model(self):
# #
# You can save a model here. # You can save a model here.
# For example, add the string: self.mdl.sexpr() # For example, add the string: self.model.sexpr()
# to a file, or print bounds in custom format. # to a file, or print bounds in custom format.
# #
# print(f"Bound: {self.lo}") # print(f"Bound: {self.lo}")
# for f in self.original_soft: # for f in self.soft.original_soft:
# print(f"{f} := {self.mdl.eval(f)}") # print(f"{f} := {self.model.eval(f)}")
pass pass
def add_pattern(self, orig_cs):
named = { f"{f}" : f for f in self.soft.original_soft }
sorted_names = sorted(named.keys())
sorted_soft = [named[f] for f in sorted_names]
bits = [1 if f not in orig_cs else 0 for f in sorted_soft]
def eq_bits(b1, b2):
return all(b1[i] == b2[i] for i in range(len(b1)))
def num_overlaps(b1, b2):
return sum(b1[i] == b2[i] for i in range(len(b1)))
if not any(eq_bits(b, bits) for b in self.patterns):
if len(self.patterns) > 0:
print(num_overlaps(bits, self.patterns[-1]), len(bits), bits)
self.patterns += [bits]
counts = [sum(b[i] for b in self.patterns) for i in range(len(bits))]
print(counts)
def improve(self, new_model): def improve(self, new_model):
mss = { f for f in self.soft.formulas if is_true(new_model.eval(f)) } mss = { f for f in self.soft.formulas if is_true(new_model.eval(f)) }
cs = set(self.soft.formulas) - mss cs = self.soft.formulas - mss
self.Cs += [cs] self.Cs += [cs]
cost = len(cs) orig_cs = { f for f in self.soft.original_soft if not is_true(new_model.eval(f)) }
if self.mdl is None: cost = len(orig_cs)
self.mdl = new_model if self.model is None:
self.model = new_model
if cost <= self.hi: if cost <= self.hi:
self.add_pattern(orig_cs)
print("improve", self.hi, cost) print("improve", self.hi, cost)
self.mdl = new_model self.model = new_model
self.save_model() self.save_model()
if cost < self.hi: if cost < self.hi:
self.hi = cost self.hi = cost
assert self.mdl assert self.model
def local_mss(self, hi, new_model): def local_mss(self, new_model):
mss = { f for f in self.soft.formulas if is_true(new_model.eval(f)) } mss = { f for f in self.soft.formulas if is_true(new_model.eval(f)) }
ps = set(self.soft.formulas) - mss ps = self.soft.formulas - mss
backbones = set() backbones = set()
qs = set() qs = set()
backbone2core = {}
while len(ps) > 0: while len(ps) > 0:
p = random.choice([p for p in ps]) p = random.choice([p for p in ps])
ps = ps - { p } ps = ps - { p }
@ -249,30 +288,43 @@ class HsMaxSAT:
qs = qs - rs qs = qs - rs
self.improve(mdl) self.improve(mdl)
elif is_sat == unsat: elif is_sat == unsat:
core = set()
for c in self.s.unsat_core():
if c in backbone2core:
core = core | backbone2core[c]
elif not p.eq(c):
core = core | { c }
self.Ks += [core]
backbone2core[Not(p)] = core
backbones = backbones | { Not(p) } backbones = backbones | { Not(p) }
else: else:
qs = qs | { p } qs = qs | { p }
if len(qs) > 0: if len(qs) > 0:
print("Number undetermined", len(qs)) print("Number undetermined", len(qs))
def get_cores(self, hs): def get_cores(self, hs):
core = self.s.unsat_core() core = self.s.unsat_core()
remaining = set(self.soft.formulas) - set(core) - set(hs) remaining = self.soft.formulas - hs
num_cores = 0 num_cores = 0
cores = [core] cores = [core]
if len(core) == 0: if len(core) == 0:
self.lo = self.hi self.lo = self.hi - self.soft.offset
return [] return
print("new core of size", len(core)) print("new core of size", len(core))
while True: while True:
is_sat = self.s.check(remaining) is_sat = self.s.check(remaining)
if unsat == is_sat: if unsat == is_sat:
core = self.s.unsat_core() core = self.s.unsat_core()
print("new core of size", len(core)) print("new core of size", len(core))
if len(core) == 0:
self.lo = self.hi - self.soft.offset
return
cores += [core] cores += [core]
remaining = remaining - set(core) h = random.choice([c for c in core])
remaining = remaining - { h }
elif sat == is_sat and num_cores == len(cores): elif sat == is_sat and num_cores == len(cores):
self.local_mss(self.hi, self.s.model()) self.local_mss(self.s.model())
break break
elif sat == is_sat: elif sat == is_sat:
self.improve(self.s.model()) self.improve(self.s.model())
@ -283,37 +335,33 @@ class HsMaxSAT:
# The new hitting set contains at least one new element # The new hitting set contains at least one new element
# from the original cores # from the original cores
# #
hs = set(hs) hs = hs | { random.choice([c for c in cores[i]]) for i in range(num_cores, len(cores)) }
for i in range(num_cores, len(cores)): remaining = self.soft.formulas - hs
h = random.choice([c for c in cores[i]])
hs = hs | { h }
remaining = set(self.soft.formulas) - set(core) - set(hs)
num_cores = len(cores) num_cores = len(cores)
else: else:
print(is_sat) print(is_sat)
break break
return cores self.Ks += [set(core) for core in cores]
print("total number of cores", len(self.Ks))
print("total number of correction sets", len(self.Cs))
def step(self): def step(self):
soft = self.soft soft = self.soft
hs = self.pick_hs() hs = self.pick_hs()
is_sat = self.s.check(set(soft.formulas) - set(hs)) is_sat = self.s.check(soft.formulas - set(hs))
if is_sat == sat: if is_sat == sat:
self.improve(self.s.model()) self.improve(self.s.model())
elif is_sat == unsat: elif is_sat == unsat:
cores = self.get_cores(hs) self.get_cores(hs)
self.Ks += [set(core) for core in cores]
print("total number of cores", len(self.Ks))
print("total number of correction sets", len(self.Cs))
else: else:
print("unknown") print("unknown")
print("maxsat [", self.lo + soft.offset, ", ", self.hi + soft.offset, "]","offset", soft.offset) print("maxsat [", self.lo + soft.offset, ", ", self.hi, "]","offset", soft.offset)
count_sets_by_size(self.Ks) count_sets_by_size(self.Ks)
count_sets_by_size(self.Cs) count_sets_by_size(self.Cs)
self.maxres() self.maxres()
def run(self): def run(self):
while self.lo < self.hi: while self.lo + self.soft.offset < self.hi:
self.step() self.step()

View file

@ -79,7 +79,6 @@ namespace seq {
bool branch_unit_variable(eqr const& e); bool branch_unit_variable(eqr const& e);
bool branch_unit_variable(expr* X, expr_ref_vector const& units);
/** /**
@ -156,6 +155,8 @@ namespace seq {
bool can_align_from_lhs_aux(expr_ref_vector const& ls, expr_ref_vector const& rs); bool can_align_from_lhs_aux(expr_ref_vector const& ls, expr_ref_vector const& rs);
bool can_align_from_rhs_aux(expr_ref_vector const& ls, expr_ref_vector const& rs); bool can_align_from_rhs_aux(expr_ref_vector const& ls, expr_ref_vector const& rs);
bool branch_unit_variable(expr* X, expr_ref_vector const& units);
}; };

View file

@ -125,145 +125,6 @@ expr* theory_seq::expr2rep(expr* e) {
return ctx.get_enode(e)->get_root()->get_expr(); return ctx.get_enode(e)->get_root()->get_expr();
} }
#if 0
/**
\brief
This step performs destructive superposition
Based on the implementation it would do the following:
e: l1 + l2 + l3 + l = r1 + r2 + r
G |- len(l1) = len(l2) = len(r1) = 0
e': l1 + l2 + l3 + l = r3 + r' occurs prior to e among equations
G |- len(r3) = len(r2)
r2, r3 are not identical
----------------------------------
e'' : r3 + r' = r1 + r2 + r
e: l1 + l2 + l3 + l = r1 + r2 + r
G |- len(l1) = len(l2) = len(r1) = 0
e': l1 + l2 + l3 + l = r3 + r' occurs prior to e among equations
G |- len(r3) = len(r2) + offset
r2, r3 are not identical
----------------------------------
e'' : r3 + r' = r1 + r2 + r
NB, this doesn't make sense because e'' is just e', which already occurs.
It doesn't inherit the constraints from e either, which would get lost.
NB, if len(r3) = len(r2) would be used, then the justification for the equality
needs to be tracked in dependencies.
TODO: propagate length offsets for last vars
*/
bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned idx,
dependency*& deps, expr_ref_vector & res) {
// disabled until functionality is clarified
return false;
if (ls.empty() || rs.empty())
return false;
expr* l_fst = find_fst_non_empty_var(ls);
expr* r_fst = find_fst_non_empty_var(rs);
if (!r_fst) return false;
expr_ref len_r_fst = mk_len(r_fst);
expr_ref len_l_fst(m);
enode * root2;
if (!ctx.e_internalized(len_r_fst)) {
return false;
}
if (l_fst) {
len_l_fst = mk_len(l_fst);
}
root2 = get_root(len_r_fst);
// Offset = 0, No change
if (l_fst && get_root(len_l_fst) == root2) {
TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst, m) << ")\n";);
return false;
}
// Offset = 0, Changed
for (unsigned i = 0; i < idx; ++i) {
depeq const& e = m_eqs[i];
if (e.ls != ls) continue;
expr* nl_fst = nullptr;
if (e.rs.size() > 1 && is_var(e.rs.get(0)))
nl_fst = e.rs.get(0);
if (nl_fst && nl_fst != r_fst && root2 == get_root(mk_len(nl_fst))) {
res.reset();
res.append(e.rs);
deps = m_dm.mk_join(e.dep(), deps);
return true;
}
}
// Offset != 0, No change
if (l_fst && ctx.e_internalized(len_l_fst)) {
enode * root1 = get_root(len_l_fst);
if (m_offset_eq.contains(root1, root2)) {
TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";);
return false;
}
}
// Offset != 0, Changed
if (m_offset_eq.contains(root2)) {
for (unsigned i = 0; i < idx; ++i) {
depeq const& e = m_eqs[i];
if (e.ls != ls) continue;
expr* nl_fst = nullptr;
if (e.rs.size() > 1 && is_var(e.rs.get(0)))
nl_fst = e.rs.get(0);
if (nl_fst && nl_fst != r_fst) {
expr_ref len_nl_fst = mk_len(nl_fst);
if (ctx.e_internalized(len_nl_fst)) {
enode * root1 = get_root(len_nl_fst);
if (m_offset_eq.contains(root2, root1)) {
res.reset();
res.append(e.rs);
deps = m_dm.mk_join(e.dep(), deps);
return true;
}
}
}
}
}
return false;
}
int theory_seq::find_fst_non_empty_idx(expr_ref_vector const& xs) {
for (unsigned i = 0; i < xs.size(); ++i) {
expr* x = xs[i];
if (!is_var(x))
return -1;
expr_ref e = mk_len(x);
if (ctx.e_internalized(e)) {
enode* root = ctx.get_enode(e)->get_root();
rational val;
if (m_autil.is_numeral(root->get_expr(), val) && val.is_zero()) {
continue;
}
}
return i;
}
return -1;
}
expr* theory_seq::find_fst_non_empty_var(expr_ref_vector const& x) {
int i = find_fst_non_empty_idx(x);
if (i >= 0)
return x[i];
return nullptr;
}
#endif
bool theory_seq::has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & offset) { bool theory_seq::has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & offset) {
if (ls.empty() || rs.empty()) if (ls.empty() || rs.empty())
@ -597,7 +458,8 @@ bool theory_seq::branch_binary_variable(depeq const& e) {
if (lenX <= rational(ys.size())) { if (lenX <= rational(ys.size())) {
expr_ref_vector Ys(m); expr_ref_vector Ys(m);
Ys.append(ys.size(), ys.c_ptr()); Ys.append(ys.size(), ys.c_ptr());
if (branch_unit_variable(e.dep(), x, Ys)) m_eq_deps = e.dep();
if (m_eq.branch_unit_variable(x, Ys))
return true; return true;
} }
expr_ref le(m_autil.mk_le(mk_len(x), m_autil.mk_int(ys.size())), m); expr_ref le(m_autil.mk_le(mk_len(x), m_autil.mk_int(ys.size())), m);
@ -625,67 +487,17 @@ bool theory_seq::branch_binary_variable(depeq const& e) {
bool theory_seq::branch_unit_variable() { bool theory_seq::branch_unit_variable() {
bool result = false; bool result = false;
for (auto const& e : m_eqs) { for (auto const& e : m_eqs) {
#if 0 seq::eqr er(e.ls, e.rs);
eqr er(e.ls, e.rs); m_eq_deps = e.dep();
m_eq_deps = e.deps;
if (m_eq.branch(0, er)) { if (m_eq.branch(0, er)) {
result = true; result = true;
break; break;
} }
#else
if (is_unit_eq(e.ls, e.rs) &&
branch_unit_variable(e.dep(), e.ls[0], e.rs)) {
result = true;
break;
}
if (is_unit_eq(e.rs, e.ls) &&
branch_unit_variable(e.dep(), e.rs[0], e.ls)) {
result = true;
break;
}
#endif
} }
CTRACE("seq", result, tout << "branch unit variable\n";); CTRACE("seq", result, tout << "branch unit variable\n";);
return result; return result;
} }
bool theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector const& units) {
SASSERT(is_var(X));
rational lenX;
if (!get_length(X, lenX)) {
TRACE("seq", tout << "enforce length on " << mk_bounded_pp(X, m, 2) << "\n";);
add_length_to_eqc(X);
return true;
}
if (lenX > rational(units.size())) {
expr_ref le(m_autil.mk_le(mk_len(X), m_autil.mk_int(units.size())), m);
TRACE("seq", tout << "propagate length on " << mk_bounded_pp(X, m, 2) << "\n";);
propagate_lit(dep, 0, nullptr, mk_literal(le));
return true;
}
SASSERT(lenX.is_unsigned());
unsigned lX = lenX.get_unsigned();
if (lX == 0) {
TRACE("seq", tout << "set empty length " << mk_bounded_pp(X, m, 2) << "\n";);
set_empty(X);
return true;
}
literal lit = mk_eq(m_autil.mk_int(lX), mk_len(X), false);
switch (ctx.get_assignment(lit)) {
case l_true: {
expr_ref R = mk_concat(lX, units.c_ptr(), X->get_sort());
return propagate_eq(dep, lit, X, R);
}
case l_undef:
TRACE("seq", tout << "set phase " << mk_pp(X, m) << "\n";);
ctx.mark_as_relevant(lit);
ctx.force_phase(lit);
return true;
default:
return false;
}
}
bool theory_seq::branch_ternary_variable() { bool theory_seq::branch_ternary_variable() {
for (auto const& e : m_eqs) { for (auto const& e : m_eqs) {
@ -1337,3 +1149,143 @@ bool theory_seq::solve_nth_eq(expr_ref_vector const& ls, expr_ref_vector const&
} }
#if 0
/**
\brief
This step performs destructive superposition
Based on the implementation it would do the following:
e: l1 + l2 + l3 + l = r1 + r2 + r
G |- len(l1) = len(l2) = len(r1) = 0
e': l1 + l2 + l3 + l = r3 + r' occurs prior to e among equations
G |- len(r3) = len(r2)
r2, r3 are not identical
----------------------------------
e'' : r3 + r' = r1 + r2 + r
e: l1 + l2 + l3 + l = r1 + r2 + r
G |- len(l1) = len(l2) = len(r1) = 0
e': l1 + l2 + l3 + l = r3 + r' occurs prior to e among equations
G |- len(r3) = len(r2) + offset
r2, r3 are not identical
----------------------------------
e'' : r3 + r' = r1 + r2 + r
NB, this doesn't make sense because e'' is just e', which already occurs.
It doesn't inherit the constraints from e either, which would get lost.
NB, if len(r3) = len(r2) would be used, then the justification for the equality
needs to be tracked in dependencies.
TODO: propagate length offsets for last vars
*/
bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned idx,
dependency*& deps, expr_ref_vector & res) {
// disabled until functionality is clarified
return false;
if (ls.empty() || rs.empty())
return false;
expr* l_fst = find_fst_non_empty_var(ls);
expr* r_fst = find_fst_non_empty_var(rs);
if (!r_fst) return false;
expr_ref len_r_fst = mk_len(r_fst);
expr_ref len_l_fst(m);
enode * root2;
if (!ctx.e_internalized(len_r_fst)) {
return false;
}
if (l_fst) {
len_l_fst = mk_len(l_fst);
}
root2 = get_root(len_r_fst);
// Offset = 0, No change
if (l_fst && get_root(len_l_fst) == root2) {
TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst, m) << ")\n";);
return false;
}
// Offset = 0, Changed
for (unsigned i = 0; i < idx; ++i) {
depeq const& e = m_eqs[i];
if (e.ls != ls) continue;
expr* nl_fst = nullptr;
if (e.rs.size() > 1 && is_var(e.rs.get(0)))
nl_fst = e.rs.get(0);
if (nl_fst && nl_fst != r_fst && root2 == get_root(mk_len(nl_fst))) {
res.reset();
res.append(e.rs);
deps = m_dm.mk_join(e.dep(), deps);
return true;
}
}
// Offset != 0, No change
if (l_fst && ctx.e_internalized(len_l_fst)) {
enode * root1 = get_root(len_l_fst);
if (m_offset_eq.contains(root1, root2)) {
TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";);
return false;
}
}
// Offset != 0, Changed
if (m_offset_eq.contains(root2)) {
for (unsigned i = 0; i < idx; ++i) {
depeq const& e = m_eqs[i];
if (e.ls != ls) continue;
expr* nl_fst = nullptr;
if (e.rs.size() > 1 && is_var(e.rs.get(0)))
nl_fst = e.rs.get(0);
if (nl_fst && nl_fst != r_fst) {
expr_ref len_nl_fst = mk_len(nl_fst);
if (ctx.e_internalized(len_nl_fst)) {
enode * root1 = get_root(len_nl_fst);
if (m_offset_eq.contains(root2, root1)) {
res.reset();
res.append(e.rs);
deps = m_dm.mk_join(e.dep(), deps);
return true;
}
}
}
}
}
return false;
}
int theory_seq::find_fst_non_empty_idx(expr_ref_vector const& xs) {
for (unsigned i = 0; i < xs.size(); ++i) {
expr* x = xs[i];
if (!is_var(x))
return -1;
expr_ref e = mk_len(x);
if (ctx.e_internalized(e)) {
enode* root = ctx.get_enode(e)->get_root();
rational val;
if (m_autil.is_numeral(root->get_expr(), val) && val.is_zero()) {
continue;
}
}
return i;
}
return -1;
}
expr* theory_seq::find_fst_non_empty_var(expr_ref_vector const& x) {
int i = find_fst_non_empty_idx(x);
if (i >= 0)
return x[i];
return nullptr;
}
#endif

View file

@ -436,7 +436,6 @@ namespace smt {
bool check_length_coherence(expr* e); bool check_length_coherence(expr* e);
bool fixed_length(bool is_zero = false); bool fixed_length(bool is_zero = false);
bool fixed_length(expr* e, bool is_zero); bool fixed_length(expr* e, bool is_zero);
bool branch_unit_variable(dependency* dep, expr* X, expr_ref_vector const& units);
bool branch_variable_eq(depeq const& e); bool branch_variable_eq(depeq const& e);
bool branch_binary_variable(depeq const& e); bool branch_binary_variable(depeq const& e);
bool can_align_from_lhs(expr_ref_vector const& ls, expr_ref_vector const& rs); bool can_align_from_lhs(expr_ref_vector const& ls, expr_ref_vector const& rs);