-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Subterms Theory #8115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Subterms Theory #8115
Conversation
I can't really figure out how to link the smt parser to the rest of the machinenery, so I will stop here and try from the other side. I'll start implmenting the logic and see if it brings me back to the parser.
Very primitive, but I don't like have that much work uncommitted.
Skip adding obvious equalities or disequality
Add m_subterm to the trail
|
@microsoft-github-policy-service agree |
|
|
||
| TRACE(datatype, tout << "propagate_not_is_subterm: " << enode_pp(n, ctx) << "\n";); | ||
|
|
||
| datatype_util util(get_manager()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m_util
| TRACE(datatype, tout << "adding recursive case: " << mk_pp(arg1->get_expr(), m) << " ⊑ " | ||
| << mk_pp(s->get_expr(), m) << "\n";); | ||
| app_ref sub_app(m.mk_app(sub_decl, arg1->get_expr(), s->get_expr()), m); | ||
| ctx.internalize(sub_app, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is probably mk_literal(sub_app) from base class in smt_theory.h instead of internalize + get_bool_var.
|
I am not sure what you mean by that model generation isn't implemented. Subterm relationship is an interpreted function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a subterm theory for datatypes as part of the SMT Solving Internals course at TU Wien. The implementation adds support for subterm predicates (⊑) that can express subterm relationships within datatype terms, with reflexivity axioms automatically generated and propagation logic integrated with the existing datatype theory solver.
Key Changes
- Added subterm predicates as a new datatype operator (OP_DT_SUBTERM) with syntax support through
:subtermkeyword in SMT-LIB2 parser - Implemented propagation logic for subterm constraints including handling of true/false assignments and interaction with constructor information
- Created a custom iterator pattern (subterm_iterator) for traversing subterms of datatype expressions
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/smt/theory_datatype.h | Adds m_subterms tracking to var_data, declares subterm propagation methods, and introduces subterm_iterator class |
| src/smt/theory_datatype.cpp | Implements reflexivity axioms, propagation logic for subterm predicates, and subterm iterator functionality |
| src/parsers/smt2/smt2parser.cpp | Adds parsing support for :subterm keyword in datatype declarations |
| src/cmd_context/pdecl.h | Adds psubterm_decl class and optional m_subterm member to pdatatype_decl |
| src/cmd_context/pdecl.cpp | Implements display method and instantiation logic for subterm declarations |
| src/cmd_context/cmd_context.cpp | Registers subterm predicate function declarations in the command context |
| src/ast/datatype_decl_plugin.h | Declares subterm class, OP_DT_SUBTERM operator, and utility methods for subterm predicates |
| src/ast/datatype_decl_plugin.cpp | Implements subterm instantiation, mk_subterm factory method, and get_datatype_subterm lookup |
| src/ast/rewriter/datatype_rewriter.cpp | Adds case for OP_DT_SUBTERM (currently returns BR_FAILED, no rewriting) |
| add_subterm_predicate(a, e); | ||
| add_subterm_predicate(b, e); | ||
|
|
||
| // propagating potentially adds a lot of litteral, avoid it if we can |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in comment: "litteral" should be "literals".
| // propagating potentially adds a lot of litteral, avoid it if we can | |
| // propagating potentially adds a lot of literals, avoid it if we can |
| } | ||
|
|
||
| void theory_datatype::propagate_subterm(enode *n, bool is_true) { | ||
| force_push(); // I am faily sure I need that here |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in comment: "faily" should be "fairly".
| force_push(); // I am faily sure I need that here | |
| force_push(); // I am fairly sure I need that here |
| return BR_DONE; | ||
| } | ||
| case OP_DT_SUBTERM: | ||
| // Re rewrite yet for subterms |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in comment: "Re rewrite yet" should be "No rewrite yet".
| // Re rewrite yet for subterms | |
| // No rewrite yet for subterms |
| * | ||
| * With `predicate:='a ⊑ b'` this should be called with `v:='a'` and `v:='b'`. | ||
| * | ||
| * This doesn't handle potential propagation. The responsability for it |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in comment: "responsability" should be "responsibility".
| * This doesn't handle potential propagation. The responsability for it | |
| * This doesn't handle potential propagation. The responsibility for it |
| subterm_iterator(const subterm_iterator&) = delete; | ||
|
|
||
| subterm_iterator begin() { | ||
| return std::move(*this); | ||
| } | ||
| subterm_iterator end() { |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The iterator pattern has a bug. When used in a range-based for loop (line 723), begin() moves from the iterator object, leaving it in a moved-from state. Then end() is called on the same moved-from object. While this might work in practice because end() only checks m_current, it's undefined behavior to use a moved-from object. The proper pattern would be to either: (1) have begin() and end() return separate iterator objects, not move from this; or (2) make this a proper range type with separate iterator and sentinel types.
| subterm_iterator(const subterm_iterator&) = delete; | |
| subterm_iterator begin() { | |
| return std::move(*this); | |
| } | |
| subterm_iterator end() { | |
| subterm_iterator(const subterm_iterator& other) | |
| : m_todo(other.m_todo), | |
| m_marked(other.m_marked), | |
| m_manager(other.m_manager), | |
| m_current(other.m_current) { | |
| } | |
| subterm_iterator begin() const { | |
| return *this; | |
| } | |
| subterm_iterator end() const { |
| #include "theory_datatype.h" | ||
|
|
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant include: theory_datatype.h is already included on line 25, so the duplicate include on line 29 should be removed.
| #include "theory_datatype.h" |
| friend class pdecl_manager; | ||
| friend class pdatatype_decl; | ||
| symbol m_name; | ||
| ptype m_type; |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The psubterm_decl class declares a member variable m_type on line 237, but it is never initialized or used. This appears to be a copy-paste artifact from paccessor_decl. Either this member should be removed, or if it's intended for future use, it should be initialized in the constructor.
| ptype m_type; |
| void theory_datatype::assert_subterm_axioms(enode * n) { | ||
| sort * s = n->get_sort(); | ||
| if (m_util.is_datatype(s)) { | ||
| func_decl * sub_decl = m_util.get_datatype_subterm(s); | ||
| if (sub_decl) { | ||
| TRACE(datatype, tout << "asserting reflexivity for #" << n->get_owner_id() << " " << mk_pp(n->get_expr(), m) << "\n";); | ||
| app_ref reflex(m.mk_app(sub_decl, n->get_expr(), n->get_expr()), m); | ||
| ctx.internalize(reflex, false); | ||
| literal l(ctx.get_bool_var(reflex)); | ||
| ctx.mark_as_relevant(l); | ||
| ctx.mk_th_axiom(get_id(), 1, &l); | ||
| } | ||
| } | ||
| } |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The subterm theory feature lacks test coverage. Given that the repository has comprehensive test coverage for datatype functionality (see src/test/parametric_datatype.cpp, src/test/simplifier.cpp with test_datatypes(), etc.), the new subterm predicates should have corresponding tests to verify correct behavior, including basic reflexivity, transitivity checks, propagation with constructors, and conflict detection.
This pull request introduces a subterm theory for datatypes, developed as part of the 199.029 SMT Solving Internals course at TU Wien.
The implementation seems functional to my testing, though there are known limitations and areas for improvement:
z3.