3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-06-11 03:15:36 +00:00

Add SMT-LIB choice support via array OP_CHOICE and instantiate choice axioms in array solvers (#9649)

This change wires SMT-LIB Hilbert choice parsing to a concrete
array-theory operator and ensures both array backends enforce the
expected semantic axiom. Previously, `(choice ((x T)) phi)` parsed as
NYI and had no solver-side instantiation path.

- **Parser: lower `choice_k` into array `OP_CHOICE`**
- `pop_quant_frame(choice_k)` now builds `(choice p)` instead of
throwing.
- Added parser include/use of array utilities to construct the term
directly from the generated lambda predicate.

- **Array decl plugin: add `OP_CHOICE` typing + surface syntax**
  - Added declaration support for `choice` with signature:
- `(Array T Bool) -> T` (encoded as `('a -> Bool) -> 'a` in HO view).
- Added recognizer/util helpers (`is_choice`, `mk_choice`) and exposed
`"choice"` in op names.

- **SMT array theory (`theory_array_full`): instantiate choice axiom**
  - Added instantiation for each encountered `choice(p)`:
    - `forall x . p(x) => p(choice(p))`
  - Integrated into internalization/relevancy paths and statistics.

- **SAT/SMT array backend (`sat/smt/array_*`): instantiate choice
axiom**
- Added new axiom record kind for choice, internalization hook,
assertion routine, and diagnostics/stat tracking.
  - Uses the same quantified implication schema as above.

- **Regression coverage**
- Extended SMT2 parser regression with an HO `choice` example to ensure
parser/eval pipeline accepts and processes choice terms.

Example of the now-supported input:

```smt2
(set-logic HO_ALL)
(declare-sort U 0)
(declare-fun P () (-> U Bool))
(assert (exists ((x U)) (P x)))
(assert (= witness (choice ((x U)) (P x))))
```

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Copilot 2026-05-27 10:05:06 -07:00 committed by GitHub
parent 690cdd3f25
commit 51da9db615
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 124 additions and 17 deletions

View file

@ -68,6 +68,8 @@ namespace array {
return assert_extensionality(r.n->get_expr(), r.select->get_expr());
case axiom_record::kind_t::is_congruence:
return assert_congruent_axiom(r.n->get_expr(), r.select->get_expr());
case axiom_record::kind_t::is_choice:
return assert_choice_axiom(r.n->get_app());
default:
UNREACHABLE();
break;
@ -469,6 +471,27 @@ namespace array {
return ctx.propagate(e_internalize(alpha), e_internalize(beta), array_axiom());
}
bool solver::assert_choice_axiom(app* choice_term) {
++m_stats.m_num_choice_axiom;
SASSERT(a.is_choice(choice_term));
expr* pred = choice_term->get_arg(0);
sort* pred_sort = pred->get_sort();
SASSERT(a.is_array(pred_sort));
SASSERT(get_array_arity(pred_sort) == 1);
SASSERT(m.is_bool(get_array_range(pred_sort)));
sort* x_sort = get_array_domain(pred_sort, 0);
expr_ref x(m.mk_var(0, x_sort), m);
expr* args1[2] = { pred, x };
expr_ref px(a.mk_select(2, args1), m);
expr* args2[2] = { pred, choice_term };
expr_ref pc(a.mk_select(2, args2), m);
expr_ref body(m.mk_implies(px, pc), m);
symbol x_name("x");
expr_ref q(m.mk_forall(1, &x_sort, &x_name, body), m);
rewrite(q);
return add_unit(mk_literal(q));
}
/**
\brief assert n1 = n2 => forall vars . (n1 vars) = (n2 vars)
*/
@ -691,4 +714,3 @@ namespace array {
}
}

View file

@ -55,6 +55,8 @@ namespace array {
return out << "extensionality " << ctx.bpp(r.n) << " " << ctx.bpp(r.select);
case axiom_record::kind_t::is_congruence:
return out << "congruence " << ctx.bpp(r.n) << " " << ctx.bpp(r.select);
case axiom_record::kind_t::is_choice:
return out << "choice " << ctx.bpp(r.n);
default:
UNREACHABLE();
}
@ -75,6 +77,7 @@ namespace array {
st.update("array def/map", m_stats.m_num_default_map_axiom);
st.update("array def/const", m_stats.m_num_default_const_axiom);
st.update("array def/store", m_stats.m_num_default_store_axiom);
st.update("array choice ax", m_stats.m_num_choice_axiom);
st.update("array ext ax", m_stats.m_num_extensionality_axiom);
st.update("array cong ax", m_stats.m_num_congruence_axiom);
st.update("array exp ax2", m_stats.m_num_select_store_axiom_delayed);

View file

@ -111,6 +111,9 @@ namespace array {
case OP_CONST_ARRAY:
internalize_lambda_eh(n);
break;
case OP_CHOICE:
push_axiom(choice_axiom(n));
break;
case OP_ARRAY_EXT:
SASSERT(is_array(n->get_arg(0)));
push_axiom(extensionality_axiom(n->get_arg(0), n->get_arg(1)));
@ -169,6 +172,8 @@ namespace array {
case OP_ARRAY_DEFAULT:
set_prop_upward(find(n->get_arg(0)));
break;
case OP_CHOICE:
break;
case OP_ARRAY_MAP:
case OP_SET_UNION:
case OP_SET_INTERSECT:
@ -255,4 +260,3 @@ namespace array {
}
}

View file

@ -43,7 +43,7 @@ namespace array {
unsigned m_num_select_const_axiom, m_num_select_store_axiom_delayed;
unsigned m_num_default_store_axiom, m_num_default_map_axiom;
unsigned m_num_default_const_axiom, m_num_default_as_array_axiom;
unsigned m_num_select_lambda_axiom;
unsigned m_num_select_lambda_axiom, m_num_choice_axiom;
void reset() { memset(this, 0, sizeof(*this)); }
stats() { reset(); }
};
@ -86,7 +86,8 @@ namespace array {
is_select,
is_extensionality,
is_default,
is_congruence
is_congruence,
is_choice
};
enum class state_t {
is_new,
@ -165,6 +166,7 @@ namespace array {
axiom_record store_axiom(euf::enode* n) { return axiom_record(axiom_record::kind_t::is_store, n); }
axiom_record extensionality_axiom(euf::enode* x, euf::enode* y) { return axiom_record(axiom_record::kind_t::is_extensionality, x, y); }
axiom_record congruence_axiom(euf::enode* a, euf::enode* b) { return axiom_record(axiom_record::kind_t::is_congruence, a, b); }
axiom_record choice_axiom(euf::enode* n) { return axiom_record(axiom_record::kind_t::is_choice, n); }
scoped_ptr<sat::constraint_base> m_constraint;
@ -176,6 +178,7 @@ namespace array {
bool assert_select_as_array_axiom(app* select, app* arr);
bool assert_select_map_axiom(app* select, app* map);
bool assert_select_lambda_axiom(app* select, expr* lambda);
bool assert_choice_axiom(app* choice_term);
bool assert_extensionality(expr* e1, expr* e2);
bool assert_default_map_axiom(app* map);
bool assert_default_const_axiom(app* cnst);