-
Notifications
You must be signed in to change notification settings - Fork 241
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
Add macro rule for capture avoidance in beta redexes and its elaboration #11513
Conversation
Found an issue in the elaboration that leads to proof holes, will need to fix. |
This ensures that we apply top-level substitutions and rewrite bodies of define-fun eagerly. This is required for #11513. In particular, currently, due to the fact that variables are *not* fresh, and the rewriter does not rewrite inside of operators, beta redexes could appear arbtirarily nested in APPLY_UF. As a result, we were requiring the fairly sophisticated "shadow elimination" methods for handling benchmarks such as: ``` (define-fun P ((x Int)) Bool false) (define-fun Q ((x Int)) Bool (P x)) (assert (Q 0)) (check-sat) ``` In particular, `Q` would be defined to be the lambda `(lambda ((x Int)) (@ (lambda ((x Int)) false) x))`, which assuming the rewriter does not rewrite inside of operators, leads us to requiring shadow elimination to do beta reduction. We previously made a nested call from within the rewriter to ensure such shadowing was resolved prior to beta reduction. The PR #11329 refactored this so that beta redexes for non-rewritten lambdas were lifted to HO terms so that the rewriter could track proofs without being self referential. However, as a downside, we now require HO reasoning in first order logics, in particular, lambdas could be rewritten and beta reduction could be done for HO applications. The PR #11513 addresses this shortcoming by allowing non-rewritten lambdas to be beta-reduced. However, proof elaboration fails since this requires arbitrarily nested shadow elimination. In particular, a proof of the above benchmark would (unintuitively) introduce shadowing elimination + alpha equivalence. The solution is to apply top-level substitutions + rewriting to *bodies* of lambdas as they are defined. this prevents HO rewriting while eliminating complications due to shadowing in beta redexes. We do not rewrite defined constants, since this complication does not arise and it better to be lazy based on some benchmarks in SMT-LIB (cpachecker in QF_UFLRA). There are several motivations for this change: 1. It makes the proofs significantly simpler and resolves all open issues related to define-fun (after #11513 is merged), 2. It makes the output of e.g. `-o post-asserts` much more intuitive. In particular, prior to this commit, we'd get: ``` (set-logic ALL) (define-fun Q ((x Int)) Bool (@ (lambda ((x Int)) false) x)) (define-fun P ((x Int)) Bool false) (assert false) (check-sat) ``` whereas now we get: ``` (set-logic ALL) (define-fun Q ((x Int)) Bool false) (define-fun P ((x Int)) Bool false) (assert false) (check-sat) ```
* where :math:`\sigma` replaces :math:`x_1, \dots, x_n` by | ||
* :math:`y_1, \dots, y_n`. The terms may either be of kind | ||
* `cvc5::Kind::APPLY_UF` or `cvc5::Kind::HO_APPLY`. | ||
* This rule ensures that :math:`y_1, \dots, y_n` are not in the free | ||
* variables of :math:`t_1 \ldots t_n` and | ||
* :math:`(\lambda x_1 \ldots x_n.\> t)` is alpha-equivalent to |
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.
Isn't it the case that the lambda expressions are necessarily gonna be alpha-equivalent when \sigma
is a valid renaming, which is the consequence of the condition above?
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.
Not if t
had a variable in y_1 ... y_n
that was not in x_1 ... x_n
.
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.
Well, t
will not, since y1...yn
are not among the free variables of the lambda expression. If you properly constrain the substitution you'll gen an alpha equivalent term, which is what you want.
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 more consice to say they are alpha equivalent, which implies this condition as well as saying that y_1 ... y_n
are unique.
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.
You do not need to say that they are alpha equivalent if you say the renaming is such that it does not include free variables from the lambda expression. You could say:
This rule requires that :math:`y_1, \dots, y_n` are pairwise distinct and not in the free variables of :math:`(\lambda x_1 \ldots x_n.\> t)`.
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's not that simple, I've made the description more accurate now.
Examples of the rule:
(_ (lambda x. forall x. P(x)) a) ---> (_ (lambda y. forall x. P(x)) a)
.
(_ (lambda y. forall x. P(y)) (f x)) ---> (_ (lambda y. forall z. P(y)) (f x))
.
(_ (lambda y. forall xu. P(y, u)) (f x)) ---> (_ (lambda y. forall zu. P(y, u)) (f x))
.
(_ (lambda y. forall x. P(y) ^ forall x. Q(y)) (f x)) ---> (_ (lambda y. forall u. P(y) ^ forall v. Q(y)) (f x))
.
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.
I see now. This rule is doing much more than I assumed it was. :) I like how you extended the documentation, thanks. I'd suggest to include an example, like the second one of the comment above.
And maybe we should consider renaming the rule, because that's what misled me: I had assumed from the name that you were just eliminating shadowing among the variables bound by the lambda, but you are in fact renaming variables in the lambda expression to avoid capture of the variables in t_1 ... t_n
(if beta-reduction is done with a not capture-avoiding substitution, which is exactly what you want...).
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.
MACRO_LAMBDA_CAPTURE_AVOID
?
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.
Sounds good!
This ensures that we apply top-level substitutions and rewrite bodies of define-fun eagerly. This is required for cvc5#11513. In particular, currently, due to the fact that variables are *not* fresh, and the rewriter does not rewrite inside of operators, beta redexes could appear arbtirarily nested in APPLY_UF. As a result, we were requiring the fairly sophisticated "shadow elimination" methods for handling benchmarks such as: ``` (define-fun P ((x Int)) Bool false) (define-fun Q ((x Int)) Bool (P x)) (assert (Q 0)) (check-sat) ``` In particular, `Q` would be defined to be the lambda `(lambda ((x Int)) (@ (lambda ((x Int)) false) x))`, which assuming the rewriter does not rewrite inside of operators, leads us to requiring shadow elimination to do beta reduction. We previously made a nested call from within the rewriter to ensure such shadowing was resolved prior to beta reduction. The PR cvc5#11329 refactored this so that beta redexes for non-rewritten lambdas were lifted to HO terms so that the rewriter could track proofs without being self referential. However, as a downside, we now require HO reasoning in first order logics, in particular, lambdas could be rewritten and beta reduction could be done for HO applications. The PR cvc5#11513 addresses this shortcoming by allowing non-rewritten lambdas to be beta-reduced. However, proof elaboration fails since this requires arbitrarily nested shadow elimination. In particular, a proof of the above benchmark would (unintuitively) introduce shadowing elimination + alpha equivalence. The solution is to apply top-level substitutions + rewriting to *bodies* of lambdas as they are defined. this prevents HO rewriting while eliminating complications due to shadowing in beta redexes. We do not rewrite defined constants, since this complication does not arise and it better to be lazy based on some benchmarks in SMT-LIB (cpachecker in QF_UFLRA). There are several motivations for this change: 1. It makes the proofs significantly simpler and resolves all open issues related to define-fun (after cvc5#11513 is merged), 2. It makes the output of e.g. `-o post-asserts` much more intuitive. In particular, prior to this commit, we'd get: ``` (set-logic ALL) (define-fun Q ((x Int)) Bool (@ (lambda ((x Int)) false) x)) (define-fun P ((x Int)) Bool false) (assert false) (check-sat) ``` whereas now we get: ``` (set-logic ALL) (define-fun Q ((x Int)) Bool false) (define-fun P ((x Int)) Bool false) (assert false) (check-sat) ```
This ensures that we apply top-level substitutions and rewrite bodies of define-fun eagerly. This is required for cvc5#11513. In particular, currently, due to the fact that variables are *not* fresh, and the rewriter does not rewrite inside of operators, beta redexes could appear arbtirarily nested in APPLY_UF. As a result, we were requiring the fairly sophisticated "shadow elimination" methods for handling benchmarks such as: ``` (define-fun P ((x Int)) Bool false) (define-fun Q ((x Int)) Bool (P x)) (assert (Q 0)) (check-sat) ``` In particular, `Q` would be defined to be the lambda `(lambda ((x Int)) (@ (lambda ((x Int)) false) x))`, which assuming the rewriter does not rewrite inside of operators, leads us to requiring shadow elimination to do beta reduction. We previously made a nested call from within the rewriter to ensure such shadowing was resolved prior to beta reduction. The PR cvc5#11329 refactored this so that beta redexes for non-rewritten lambdas were lifted to HO terms so that the rewriter could track proofs without being self referential. However, as a downside, we now require HO reasoning in first order logics, in particular, lambdas could be rewritten and beta reduction could be done for HO applications. The PR cvc5#11513 addresses this shortcoming by allowing non-rewritten lambdas to be beta-reduced. However, proof elaboration fails since this requires arbitrarily nested shadow elimination. In particular, a proof of the above benchmark would (unintuitively) introduce shadowing elimination + alpha equivalence. The solution is to apply top-level substitutions + rewriting to *bodies* of lambdas as they are defined. this prevents HO rewriting while eliminating complications due to shadowing in beta redexes. We do not rewrite defined constants, since this complication does not arise and it better to be lazy based on some benchmarks in SMT-LIB (cpachecker in QF_UFLRA). There are several motivations for this change: 1. It makes the proofs significantly simpler and resolves all open issues related to define-fun (after cvc5#11513 is merged), 2. It makes the output of e.g. `-o post-asserts` much more intuitive. In particular, prior to this commit, we'd get: ``` (set-logic ALL) (define-fun Q ((x Int)) Bool (@ (lambda ((x Int)) false) x)) (define-fun P ((x Int)) Bool false) (assert false) (check-sat) ``` whereas now we get: ``` (set-logic ALL) (define-fun Q ((x Int)) Bool false) (define-fun P ((x Int)) Bool false) (assert false) (check-sat) ```
…ion (cvc5#11513) This adds a macro rewrite to capture some of the rewrites in the UF rewriter and adds proof elaboration for it, which is expected to be accomplished by (possibly multiple steps of) alpha equivalence. Note that it uses a more elegant solution than cvc5#11329, which lifted APPLY_UF to higher-order applications to ensure shadowing was eliminated. Now, the rewriting the APPLY_UF is done "in-place", i.e. `(APPLY_UF L ts) ---> (APPLY_UF L' ts)`. As a result, we revert to a behavior where HO_APPLY is never introduced in first order logics, making the change to TheoryUF unecessary. It also means we don't rely on higher-order beta-reduction in first-order logics. This reenables `alethe` on these regressions. This rewrite is used to ensure that beta reduction does not introduce capture avoiding substitutions. The UF rewriter is refactored to use the reference implementation. Adds a new utility method (getMatchConds) to node_algorith.h which will be used for a few other pending tasks in proof elaboration. Fills 1 proof hole in our regressions and 287 proof holes on SMT-LIB.
This adds a macro rewrite to capture some of the rewrites in the UF rewriter and adds proof elaboration for it, which is expected to be accomplished by (possibly multiple steps of) alpha equivalence.
Note that it uses a more elegant solution than #11329, which lifted APPLY_UF to higher-order applications to ensure shadowing was eliminated. Now, the rewriting the APPLY_UF is done "in-place", i.e.
(APPLY_UF L ts) ---> (APPLY_UF L' ts)
. As a result, we revert to a behavior where HO_APPLY is never introduced in first order logics, making the change to TheoryUF unecessary. It also means we don't rely on higher-order beta-reduction in first-order logics. This reenablesalethe
on these regressions.This rewrite is used to ensure that beta reduction does not introduce capture avoiding substitutions. The UF rewriter is refactored to use the reference implementation.
Adds a new utility method (getMatchConds) to node_algorith.h which will be used for a few other pending tasks in proof elaboration.
Fills 1 proof hole in our regressions and 287 proof holes on SMT-LIB.