mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-03 21:09:11 +00:00 
			
		
		
		
	overhaul stoi and itos to fix #1957 and related
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									801026937d
								
							
						
					
					
						commit
						88fb826a03
					
				
					 3 changed files with 106 additions and 97 deletions
				
			
		| 
						 | 
				
			
			@ -428,7 +428,18 @@ public:
 | 
			
		|||
                            add(mv);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (1 == out_degree(src) && (is_final_state(src) || !is_final_state(dst))) {
 | 
			
		||||
                        moves const& mvs = m_delta[dst];
 | 
			
		||||
                        moves mvs1;
 | 
			
		||||
                        for (move const& mv : mvs) {
 | 
			
		||||
                            mvs1.push_back(move(m, src, mv.dst(), mv.t()));
 | 
			
		||||
                        }
 | 
			
		||||
                        for (move const& mv : mvs1) {
 | 
			
		||||
                            add(mv);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        TRACE("seq", tout << "epsilon not removed " << out_degree(src) << " " << is_final_state(src) << " " << is_final_state(dst) << "\n";);
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }                    
 | 
			
		||||
                    remove(src, dst, nullptr);
 | 
			
		||||
| 
						 | 
				
			
			@ -600,13 +611,14 @@ private:
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    void remove_dead_states() {
 | 
			
		||||
        unsigned_vector remap;
 | 
			
		||||
        for (unsigned i = 0; i < m_delta.size(); ++i) {
 | 
			
		||||
            
 | 
			
		||||
        }
 | 
			
		||||
    }    
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    void add(move const& mv) {
 | 
			
		||||
        if (!is_duplicate_cheap(mv)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3431,14 +3431,17 @@ void theory_seq::add_stoi_axiom(expr* e) {
 | 
			
		|||
    literal l = mk_simplified_literal(m_autil.mk_ge(e, m_autil.mk_int(-1)));
 | 
			
		||||
    add_axiom(l);    
 | 
			
		||||
    
 | 
			
		||||
    // stoi(s) >= 0 <=> s in (0-9)+
 | 
			
		||||
    expr_ref num_re(m);
 | 
			
		||||
    num_re = m_util.re.mk_range(m_util.str.mk_string(symbol("0")), m_util.str.mk_string(symbol("9")));
 | 
			
		||||
    num_re = m_util.re.mk_plus(num_re);
 | 
			
		||||
    app_ref in_re(m_util.re.mk_in_re(s, num_re), m);
 | 
			
		||||
    literal ge0 = mk_simplified_literal(m_autil.mk_ge(e, m_autil.mk_int(0)));
 | 
			
		||||
    add_axiom(~ge0, mk_literal(in_re));
 | 
			
		||||
    add_axiom(ge0, ~mk_literal(in_re));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void theory_seq::ensure_digit_axiom() {
 | 
			
		||||
 | 
			
		||||
    if (m_stoi_axioms.empty() && m_itos_axioms.empty()) {
 | 
			
		||||
        bv_util bv(m);
 | 
			
		||||
        for (unsigned i = 0; i < 10; ++i) {
 | 
			
		||||
            expr_ref cnst(bv.mk_numeral(rational('0'+i), bv.mk_sort(8)), m);
 | 
			
		||||
            add_axiom(mk_eq(digit2int(cnst), m_autil.mk_int(i), false));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool theory_seq::add_stoi_val_axiom(expr* e) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3447,54 +3450,16 @@ bool theory_seq::add_stoi_val_axiom(expr* e) {
 | 
			
		|||
    rational val;
 | 
			
		||||
    TRACE("seq", tout << mk_pp(e, m) << "\n";);
 | 
			
		||||
    VERIFY(m_util.str.is_stoi(e, n));    
 | 
			
		||||
    if (!get_num_value(e, val)) {
 | 
			
		||||
 | 
			
		||||
    if (m_util.str.is_itos(n)) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (!m_stoi_axioms.contains(val)) {
 | 
			
		||||
        m_stoi_axioms.insert(val);
 | 
			
		||||
        if (!val.is_neg()) {
 | 
			
		||||
            app_ref e1(m_util.str.mk_string(symbol(val.to_string().c_str())), m);            
 | 
			
		||||
            expr_ref n1(arith_util(m).mk_numeral(val, true), m);
 | 
			
		||||
            literal eq1 = mk_preferred_eq(e, n1);
 | 
			
		||||
            literal eq2 = mk_preferred_eq(n, e1);
 | 
			
		||||
            add_axiom(~eq1, eq2);
 | 
			
		||||
            add_axiom(~eq2, eq1);
 | 
			
		||||
            m_trail_stack.push(insert_map<theory_seq, rational_set, rational>(m_stoi_axioms, val));
 | 
			
		||||
            m_trail_stack.push(push_replay(alloc(replay_axiom, m, e)));
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (upper_bound(n, val) && get_length(n, val) && val.is_pos() && !m_stoi_axioms.contains(val)) {
 | 
			
		||||
        zstring s;
 | 
			
		||||
        SASSERT(val.is_unsigned());
 | 
			
		||||
        unsigned sz = val.get_unsigned();
 | 
			
		||||
        expr_ref len1(m), len2(m), ith_char(m), num(m), coeff(m);
 | 
			
		||||
        expr_ref_vector nums(m);
 | 
			
		||||
        len1 = m_util.str.mk_length(n);
 | 
			
		||||
        len2 = m_autil.mk_int(sz);
 | 
			
		||||
        literal lit = mk_eq(len1, len2, false);
 | 
			
		||||
        literal_vector lits;
 | 
			
		||||
        lits.push_back(~lit);
 | 
			
		||||
        for (unsigned i = 0; i < sz; ++i) {
 | 
			
		||||
            ith_char = mk_nth(n, m_autil.mk_int(i));
 | 
			
		||||
            lits.push_back(~is_digit(ith_char));
 | 
			
		||||
            nums.push_back(digit2int(ith_char));
 | 
			
		||||
        }        
 | 
			
		||||
        rational c(1);
 | 
			
		||||
        for (unsigned i = sz; i-- > 0; c *= rational(10)) {
 | 
			
		||||
            coeff = m_autil.mk_numeral(c, true);
 | 
			
		||||
            nums[i] = m_autil.mk_mul(coeff, nums[i].get());
 | 
			
		||||
        }
 | 
			
		||||
        num = m_autil.mk_add(nums.size(), nums.c_ptr());
 | 
			
		||||
        ctx.get_rewriter()(num);
 | 
			
		||||
        lits.push_back(mk_preferred_eq(e, num));
 | 
			
		||||
        ++m_stats.m_add_axiom;
 | 
			
		||||
        m_new_propagation = true;
 | 
			
		||||
        for (literal lit : lits) {
 | 
			
		||||
            ctx.mark_as_relevant(lit);
 | 
			
		||||
        }
 | 
			
		||||
        TRACE("seq", ctx.display_literals_verbose(tout, lits); tout << "\n";);
 | 
			
		||||
        ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
 | 
			
		||||
 | 
			
		||||
    enforce_length(n);
 | 
			
		||||
 | 
			
		||||
    if (get_length(n, val) && val.is_pos() && val.is_unsigned() && !m_stoi_axioms.contains(val)) {
 | 
			
		||||
        ensure_digit_axiom();
 | 
			
		||||
        add_si_axiom(n, e, val.get_unsigned());        
 | 
			
		||||
        m_stoi_axioms.insert(val);
 | 
			
		||||
        m_trail_stack.push(insert_map<theory_seq, rational_set, rational>(m_stoi_axioms, val));
 | 
			
		||||
        m_trail_stack.push(push_replay(alloc(replay_axiom, m, e)));
 | 
			
		||||
| 
						 | 
				
			
			@ -3515,16 +3480,6 @@ literal theory_seq::is_digit(expr* ch) {
 | 
			
		|||
    add_axiom(~lo, ~hi, isd);
 | 
			
		||||
    add_axiom(~isd, lo);
 | 
			
		||||
    add_axiom(~isd, hi);
 | 
			
		||||
#if 1
 | 
			
		||||
    for (unsigned i = 0; i < 10; ++i) {
 | 
			
		||||
        expr_ref cnst(bv.mk_numeral(rational('0'+i), bv.mk_sort(8)), m);
 | 
			
		||||
        add_axiom(mk_eq(digit2int(cnst), m_autil.mk_int(i), false));
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    for (unsigned i = 0; i < 10; ++i) {
 | 
			
		||||
        add_axiom(~mk_eq(ch, bv.mk_numeral(rational('0'+i), bv.mk_sort(8)), false), mk_preferred_eq(d2i, m_autil.mk_int(i)));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return isd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3553,14 +3508,46 @@ void theory_seq::add_itos_axiom(expr* e) {
 | 
			
		|||
    app_ref stoi(m_util.str.mk_stoi(e), m);
 | 
			
		||||
    add_axiom(~ge0, mk_preferred_eq(stoi, n));
 | 
			
		||||
 | 
			
		||||
    // n >= 0 => itos(n) in (0-9)+
 | 
			
		||||
    expr_ref num_re(m);
 | 
			
		||||
    num_re = m_util.re.mk_range(m_util.str.mk_string(symbol("0")), m_util.str.mk_string(symbol("9")));
 | 
			
		||||
    num_re = m_util.re.mk_plus(num_re);
 | 
			
		||||
    app_ref in_re(m_util.re.mk_in_re(e, num_re), m);
 | 
			
		||||
    add_axiom(~ge0, mk_literal(in_re));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// n >= 0 & len(e) = k => is_digit(e_i) for i = 0..k-1
 | 
			
		||||
// n >= 0 & len(e) = k => n = sum 10^i*digit(e_i)
 | 
			
		||||
// n < 0  & len(e) = k => \/_i ~is_digit(e_i) for i = 0..k-1
 | 
			
		||||
 | 
			
		||||
void theory_seq::add_si_axiom(expr* e, expr* n, unsigned k) {
 | 
			
		||||
    context& ctx = get_context();
 | 
			
		||||
    zstring s;
 | 
			
		||||
    expr_ref ith_char(m), num(m), coeff(m);
 | 
			
		||||
    expr_ref_vector nums(m), chars(m);
 | 
			
		||||
    literal len_eq_k = mk_preferred_eq(m_util.str.mk_length(e), m_autil.mk_int(k));
 | 
			
		||||
    literal ge0 = mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0)));
 | 
			
		||||
    literal_vector digits;
 | 
			
		||||
    digits.push_back(~len_eq_k);
 | 
			
		||||
    digits.push_back(ge0);
 | 
			
		||||
    for (unsigned i = 0; i < k; ++i) {
 | 
			
		||||
        ith_char = mk_nth(e, m_autil.mk_int(i));
 | 
			
		||||
        literal isd = is_digit(ith_char);
 | 
			
		||||
        add_axiom(~len_eq_k, ~ge0, isd);
 | 
			
		||||
        chars.push_back(m_util.str.mk_unit(ith_char));
 | 
			
		||||
        nums.push_back(digit2int(ith_char));
 | 
			
		||||
    }        
 | 
			
		||||
    ++m_stats.m_add_axiom;
 | 
			
		||||
    ctx.mk_th_axiom(get_id(), digits.size(), digits.c_ptr());
 | 
			
		||||
    rational c(1);
 | 
			
		||||
    for (unsigned i = k; i-- > 0; c *= rational(10)) {
 | 
			
		||||
        coeff = m_autil.mk_int(c);
 | 
			
		||||
        nums[i] = m_autil.mk_mul(coeff, nums.get(i));
 | 
			
		||||
    }
 | 
			
		||||
    num = m_autil.mk_add(nums.size(), nums.c_ptr());
 | 
			
		||||
    ctx.get_rewriter()(num);
 | 
			
		||||
    m_new_propagation = true;
 | 
			
		||||
    add_axiom(~len_eq_k, ~ge0, mk_preferred_eq(n, num));
 | 
			
		||||
 | 
			
		||||
    add_axiom(~len_eq_k, ~ge0, mk_preferred_eq(e, m_util.str.mk_concat(chars)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool theory_seq::add_itos_val_axiom(expr* e) {
 | 
			
		||||
    context& ctx = get_context();
 | 
			
		||||
    rational val;
 | 
			
		||||
| 
						 | 
				
			
			@ -3569,24 +3556,21 @@ bool theory_seq::add_itos_val_axiom(expr* e) {
 | 
			
		|||
    VERIFY(m_util.str.is_itos(e, n));
 | 
			
		||||
    bool change = false;
 | 
			
		||||
 | 
			
		||||
    if (get_num_value(n, val) && !val.is_neg() && !m_itos_axioms.contains(val)) {
 | 
			
		||||
        m_itos_axioms.insert(val);
 | 
			
		||||
        app_ref e1(m_util.str.mk_string(symbol(val.to_string().c_str())), m);            
 | 
			
		||||
        expr_ref n1(arith_util(m).mk_numeral(val, true), m);
 | 
			
		||||
        
 | 
			
		||||
        // itos(n) = "25" <=> n = 25
 | 
			
		||||
        literal eq1 = mk_eq(n1, n , false);
 | 
			
		||||
        literal eq2 = mk_eq(e, e1, false);
 | 
			
		||||
        add_axiom(~eq1, eq2);
 | 
			
		||||
        add_axiom(~eq2, eq1);
 | 
			
		||||
        ctx.force_phase(eq1);
 | 
			
		||||
        ctx.force_phase(eq2);
 | 
			
		||||
        
 | 
			
		||||
        m_trail_stack.push(insert_map<theory_seq, rational_set, rational>(m_itos_axioms, val));
 | 
			
		||||
        m_trail_stack.push(push_replay(alloc(replay_axiom, m, e)));        
 | 
			
		||||
        change = true;        
 | 
			
		||||
    if (m_util.str.is_stoi(n)) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return change;
 | 
			
		||||
    enforce_length(e);
 | 
			
		||||
 | 
			
		||||
    if (get_length(e, val) && val.is_pos() && !m_itos_axioms.contains(val) && val.is_unsigned()) {
 | 
			
		||||
        add_si_axiom(e, n, val.get_unsigned());
 | 
			
		||||
        ensure_digit_axiom();
 | 
			
		||||
        m_itos_axioms.insert(val);
 | 
			
		||||
        m_trail_stack.push(insert_map<theory_seq, rational_set, rational>(m_itos_axioms, val));
 | 
			
		||||
        m_trail_stack.push(push_replay(alloc(replay_axiom, m, e)));
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void theory_seq::apply_sort_cnstr(enode* n, sort* s) {
 | 
			
		||||
| 
						 | 
				
			
			@ -5553,8 +5537,6 @@ bool theory_seq::add_accept2step(expr* acc, bool& change) {
 | 
			
		|||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    SASSERT(m_autil.is_numeral(idx));
 | 
			
		||||
    eautomaton::moves mvs;
 | 
			
		||||
    aut->get_moves_from(src, mvs);
 | 
			
		||||
 | 
			
		||||
    expr_ref len(m_util.str.mk_length(e), m);
 | 
			
		||||
    literal_vector lits;
 | 
			
		||||
| 
						 | 
				
			
			@ -5572,29 +5554,40 @@ bool theory_seq::add_accept2step(expr* acc, bool& change) {
 | 
			
		|||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    eautomaton::moves mvs;
 | 
			
		||||
    aut->get_moves_from(src, mvs);
 | 
			
		||||
    bool has_undef = false;
 | 
			
		||||
    int start = ctx.get_random_value();
 | 
			
		||||
    TRACE("seq", tout << mvs.size() << "\n";);
 | 
			
		||||
    for (unsigned i = 0; i < mvs.size(); ++i) {
 | 
			
		||||
        unsigned j = (i + start) % mvs.size();
 | 
			
		||||
        eautomaton::move mv = mvs[j];
 | 
			
		||||
        expr_ref nth = mk_nth(e, idx);
 | 
			
		||||
        expr_ref acc = mv.t()->accept(nth);
 | 
			
		||||
        TRACE("seq", tout << j << ": " << acc << "\n";);
 | 
			
		||||
        step = mk_step(e, idx, re, src, mv.dst(), acc);
 | 
			
		||||
        lits.push_back(mk_literal(step));
 | 
			
		||||
        switch (ctx.get_assignment(lits.back())) {
 | 
			
		||||
        literal slit = mk_literal(step);
 | 
			
		||||
        literal tlit = mk_literal(acc);
 | 
			
		||||
        add_axiom(~slit, tlit);
 | 
			
		||||
        lits.push_back(slit);
 | 
			
		||||
        switch (ctx.get_assignment(slit)) {
 | 
			
		||||
        case l_true:
 | 
			
		||||
            return false;
 | 
			
		||||
        case l_undef:
 | 
			
		||||
            //ctx.force_phase(lits.back());
 | 
			
		||||
            //return true;
 | 
			
		||||
            ctx.mark_as_relevant(slit);
 | 
			
		||||
            // ctx.force_phase(slit);
 | 
			
		||||
            // return true;
 | 
			
		||||
            // std::cout << mk_pp(step, m) << " is undef\n";
 | 
			
		||||
            has_undef = true;
 | 
			
		||||
            break;
 | 
			
		||||
            break;            
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    change = true;
 | 
			
		||||
    if (has_undef && mvs.size() == 1) {
 | 
			
		||||
        TRACE("seq", tout << "has single move\n";);
 | 
			
		||||
        literal lit = lits.back();
 | 
			
		||||
        lits.pop_back();
 | 
			
		||||
        for (unsigned i = 0; i < lits.size(); ++i) {
 | 
			
		||||
| 
						 | 
				
			
			@ -5604,6 +5597,7 @@ bool theory_seq::add_accept2step(expr* acc, bool& change) {
 | 
			
		|||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (has_undef) {
 | 
			
		||||
        TRACE("seq", tout << "has undef\n";);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    TRACE("seq", ctx.display_literals_verbose(tout, lits); tout << "\n";);
 | 
			
		||||
| 
						 | 
				
			
			@ -5617,14 +5611,15 @@ bool theory_seq::add_accept2step(expr* acc, bool& change) {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   step(s, idx, re, i, j, t) => t
 | 
			
		||||
   acc(s, idx, re, i) & step(s, idx, re, i, j, t) => acc(s, idx + 1, re, j)
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
bool theory_seq::add_step2accept(expr* step, bool& change) {
 | 
			
		||||
    context& ctx = get_context();
 | 
			
		||||
    SASSERT(ctx.get_assignment(step) == l_true);
 | 
			
		||||
    expr* re = nullptr, *_acc = nullptr, *s = nullptr, *idx = nullptr, *i = nullptr, *j = nullptr;
 | 
			
		||||
    VERIFY(is_step(step, s, idx, re, i, j, _acc));
 | 
			
		||||
    expr* re = nullptr, *t = nullptr, *s = nullptr, *idx = nullptr, *i = nullptr, *j = nullptr;
 | 
			
		||||
    VERIFY(is_step(step, s, idx, re, i, j, t));
 | 
			
		||||
    literal acc1 = mk_accept(s, idx,  re, i);
 | 
			
		||||
    switch (ctx.get_assignment(acc1)) {
 | 
			
		||||
    case l_false:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -552,6 +552,8 @@ namespace smt {
 | 
			
		|||
        void add_stoi_axiom(expr* n);
 | 
			
		||||
        bool add_stoi_val_axiom(expr* n);
 | 
			
		||||
        bool add_itos_val_axiom(expr* n);
 | 
			
		||||
        void add_si_axiom(expr* s, expr* i, unsigned sz);
 | 
			
		||||
        void ensure_digit_axiom();
 | 
			
		||||
        literal is_digit(expr* ch);
 | 
			
		||||
        expr_ref digit2int(expr* ch);
 | 
			
		||||
        void add_itos_length_axiom(expr* n);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue