3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-06-24 09:30:31 +00:00

Refactor: replace int_constraint with constraint struct, promote cur_path to member, expose path leaf side constraints (#9124)

* chore: update plan with cur_path and side constraints requirements

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Z3Prover/z3/sessions/1523cf0a-b7a4-41a6-b792-7cd41b4dcd3b

* Refactor: rename int_constraint to constraint, remove int_constraint_kind enum

- Rename int_constraint struct to constraint with fields fml/dep
- Remove int_constraint_kind enum; pre-build formula expressions instead
- nielsen_edge: add_side_int/side_int() -> add_side_constraint/side_constraints()
- nielsen_node: add_int_constraint/int_constraints() -> add_constraint/constraints()
- nielsen_graph: mk_int_constraint(lhs,rhs,kind,dep) -> mk_constraint(fml,dep)
- Remove int_constraint_to_expr (no longer needed)
- search_dfs/simplify_and_init/check_int_feasibility/check_lp_le: drop cur_path param
- Add m_cur_path member to nielsen_graph; m_cur_path.reset() in solve()
- Add get_path_leaf_side_constraints() implementation
- Update seq_parikh.h/cpp and seq_nielsen_pp.cpp to use new constraint API

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: constraint struct, promote cur_path, expose path leaf side constraints

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Z3Prover/z3/sessions/1523cf0a-b7a4-41a6-b792-7cd41b4dcd3b

* fix: remove spurious includes from seq_nielsen.cpp

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Z3Prover/z3/sessions/aa283d79-cd42-4b87-aaf0-4273a8327b76

* fix: update test files to use renamed constraint API and fix inverted root guard

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Z3Prover/z3/sessions/b09bbc56-9617-4277-8e0c-27fa7e588037

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Copilot 2026-03-24 22:18:30 -07:00 committed by GitHub
parent 9f6eb4f455
commit e3e235aa7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 183 additions and 231 deletions

View file

@ -3300,26 +3300,26 @@ static void test_add_lower_int_bound_basic() {
// initially no bounds
SASSERT(node->var_lb(x) == 0);
SASSERT(node->var_ub(x) == UINT_MAX);
SASSERT(node->int_constraints().empty());
SASSERT(node->constraints().empty());
// add lower bound lb=3: should tighten and add constraint
bool tightened = node->add_lower_int_bound(x, 3, dep);
SASSERT(tightened);
SASSERT(node->var_lb(x) == 3);
SASSERT(node->int_constraints().size() == 1);
SASSERT(node->int_constraints()[0].m_kind == seq::int_constraint_kind::ge);
SASSERT(node->constraints().size() == 1);
SASSERT(node->constraints()[0].fml);
// add weaker lb=2: no tightening
tightened = node->add_lower_int_bound(x, 2, dep);
SASSERT(!tightened);
SASSERT(node->var_lb(x) == 3);
SASSERT(node->int_constraints().size() == 1);
SASSERT(node->constraints().size() == 1);
// add tighter lb=5: should tighten and add another constraint
tightened = node->add_lower_int_bound(x, 5, dep);
SASSERT(tightened);
SASSERT(node->var_lb(x) == 5);
SASSERT(node->int_constraints().size() == 2);
SASSERT(node->constraints().size() == 2);
std::cout << " ok\n";
}
@ -3347,20 +3347,20 @@ static void test_add_upper_int_bound_basic() {
bool tightened = node->add_upper_int_bound(x, 10, dep);
SASSERT(tightened);
SASSERT(node->var_ub(x) == 10);
SASSERT(node->int_constraints().size() == 1);
SASSERT(node->int_constraints()[0].m_kind == seq::int_constraint_kind::le);
SASSERT(node->constraints().size() == 1);
SASSERT(node->constraints()[0].fml);
// add weaker ub=20: no tightening
tightened = node->add_upper_int_bound(x, 20, dep);
SASSERT(!tightened);
SASSERT(node->var_ub(x) == 10);
SASSERT(node->int_constraints().size() == 1);
SASSERT(node->constraints().size() == 1);
// add tighter ub=5: tightens
tightened = node->add_upper_int_bound(x, 5, dep);
SASSERT(tightened);
SASSERT(node->var_ub(x) == 5);
SASSERT(node->int_constraints().size() == 2);
SASSERT(node->constraints().size() == 2);
std::cout << " ok\n";
}
@ -3427,7 +3427,7 @@ static void test_bounds_cloned() {
SASSERT(child->var_ub(y) == UINT_MAX);
// child's int_constraints should also be cloned (3 constraints: lb_x, ub_x, lb_y)
SASSERT(child->int_constraints().size() == parent->int_constraints().size());
SASSERT(child->constraints().size() == parent->constraints().size());
std::cout << " ok\n";
}
@ -3454,7 +3454,7 @@ static void test_var_bound_watcher_single_var() {
// set bounds: 3 <= len(x) <= 7
node->add_lower_int_bound(x, 3, dep);
node->add_upper_int_bound(x, 7, dep);
node->int_constraints().reset(); // clear for clean count
node->constraints().reset(); // clear for clean count
// apply substitution x → a·y
euf::snode* ay = sg.mk_concat(a, y);
@ -3490,7 +3490,7 @@ static void test_var_bound_watcher_conflict() {
// set bounds: 3 <= len(x) (so x must have at least 3 chars)
node->add_lower_int_bound(x, 3, dep);
node->int_constraints().reset();
node->constraints().reset();
// apply substitution x → a·b (const_len=2 < lb=3)
euf::snode* ab = sg.mk_concat(a, b);
@ -3624,7 +3624,7 @@ static void test_var_bound_watcher_multi_var() {
// set upper bound: len(x) <= 5
node->add_upper_int_bound(x, 5, dep);
node->int_constraints().reset();
node->constraints().reset();
// apply substitution x → y·z (two vars, no constants)
euf::snode* yz = sg.mk_concat(y, z);

View file

@ -326,7 +326,7 @@ static void test_generate_constraints_ab_star() {
seq::dep_tracker dep = dm.mk_leaf(lit);
seq::str_mem mem(x, regex, nullptr, 0, dep);
vector<seq::int_constraint> out;
vector<seq::constraint> out;
parikh.generate_parikh_constraints(mem, out);
// expect at least: len(x)=0+2k and k>=0
@ -337,19 +337,19 @@ static void test_generate_constraints_ab_star() {
// check that one constraint is an equality (len(x) = 0 + 2·k)
bool has_eq = false;
for (auto const& ic : out)
if (ic.m_kind == seq::int_constraint_kind::eq) has_eq = true;
if (m.is_eq(ic.fml)) has_eq = true;
SASSERT(has_eq);
// check that one constraint is k >= 0
bool has_ge = false;
for (auto const& ic : out)
if (ic.m_kind == seq::int_constraint_kind::ge) has_ge = true;
if (arith.is_ge(ic.fml)) has_ge = true;
SASSERT(has_ge);
// should NOT have an upper bound (star is unbounded)
bool has_le = false;
for (auto const& ic : out)
if (ic.m_kind == seq::int_constraint_kind::le) has_le = true;
if (arith.is_le(ic.fml)) has_le = true;
SASSERT(!has_le);
}
@ -361,6 +361,7 @@ static void test_generate_constraints_bounded_loop() {
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
arith_util arith(m);
seq::seq_parikh parikh(sg);
euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort());
@ -372,7 +373,7 @@ static void test_generate_constraints_bounded_loop() {
seq::dep_tracker dep = dm.mk_leaf(sat::null_literal);
seq::str_mem mem(x, regex, nullptr, 0, dep);
vector<seq::int_constraint> out;
vector<seq::constraint> out;
parikh.generate_parikh_constraints(mem, out);
// expect: eq + ge + le = 3 constraints
@ -381,9 +382,9 @@ static void test_generate_constraints_bounded_loop() {
bool has_eq = false, has_ge = false, has_le = false;
for (auto const& ic : out) {
if (ic.m_kind == seq::int_constraint_kind::eq) has_eq = true;
if (ic.m_kind == seq::int_constraint_kind::ge) has_ge = true;
if (ic.m_kind == seq::int_constraint_kind::le) has_le = true;
if (m.is_eq(ic.fml)) has_eq = true;
if (arith.is_ge(ic.fml)) has_ge = true;
if (arith.is_le(ic.fml)) has_le = true;
}
SASSERT(has_eq);
SASSERT(has_ge);
@ -409,7 +410,7 @@ static void test_generate_constraints_stride_one() {
seq::dep_tracker dep = dm.mk_leaf(sat::null_literal);
seq::str_mem mem(x, regex, nullptr, 0, dep);
vector<seq::int_constraint> out;
vector<seq::constraint> out;
parikh.generate_parikh_constraints(mem, out);
std::cout << " generated " << out.size() << " constraints (expect 0)\n";
SASSERT(out.empty());
@ -432,7 +433,7 @@ static void test_generate_constraints_fixed_length() {
seq::dep_tracker dep = dm.mk_leaf(sat::null_literal);
seq::str_mem mem(x, regex, nullptr, 0, dep);
vector<seq::int_constraint> out;
vector<seq::constraint> out;
parikh.generate_parikh_constraints(mem, out);
std::cout << " generated " << out.size() << " constraints (expect 0)\n";
SASSERT(out.empty());
@ -456,14 +457,14 @@ static void test_generate_constraints_dep_propagated() {
seq::dep_tracker dep = dm.mk_leaf(lit);
seq::str_mem mem(x, regex, nullptr, 0, dep);
vector<seq::int_constraint> out;
vector<seq::constraint> out;
parikh.generate_parikh_constraints(mem, out);
// all generated constraints must carry dep_source{mem,7} in their dependency
for (auto const& ic : out) {
SASSERT(ic.m_dep != nullptr);
SASSERT(ic.dep != nullptr);
vector<seq::dep_source, false> vs;
dm.linearize(ic.m_dep, vs);
dm.linearize(ic.dep, vs);
bool found = false;
for (auto const& d : vs)
if (std::get<sat::literal>(d) == lit) found = true;
@ -495,11 +496,11 @@ static void test_apply_to_node_adds_constraints() {
// root node should have no int_constraints initially
SASSERT(ng.root() != nullptr);
unsigned before = ng.root()->int_constraints().size();
unsigned before = ng.root()->constraints().size();
parikh.apply_to_node(*ng.root());
unsigned after = ng.root()->int_constraints().size();
unsigned after = ng.root()->constraints().size();
std::cout << " before=" << before << " after=" << after << "\n";
SASSERT(after > before);
}
@ -525,9 +526,9 @@ static void test_apply_to_node_stride_one_no_constraints() {
euf::snode* regex = sg.mk(re);
ng.add_str_mem(x, regex);
unsigned before = ng.root()->int_constraints().size();
unsigned before = ng.root()->constraints().size();
parikh.apply_to_node(*ng.root());
unsigned after = ng.root()->int_constraints().size();
unsigned after = ng.root()->constraints().size();
std::cout << " before=" << before << " after=" << after << " (expect no change)\n";
SASSERT(after == before);
}