-
Notifications
You must be signed in to change notification settings - Fork 33
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
Fix distinct unsoundness #890
Conversation
83e7d92
to
e81c18a
Compare
This PR quickly fixes the clash between the SMT-LIB specification of `distinct` and the implementation of this expression in AE. Basically, we expand the expression `distinct x_1 ... x_n` into a conjunction of binary disequalities. Notice that the model outputted for `uf2.models.smt2` changes because I don't preserve the old order used in `distinct`. This fix doesn't change the smart constructor `mk_distinct`. We shouldn't change the API on the branch v2.5.x and I add a comment to advertise the user that this smart constructor doesn't behave as expected.
I disagree on this point. (One other way to think about it: if we have a function |
Besides, I checked that the constructor |
b164013
to
c55c086
Compare
The smart construct `mk_distinct` produces an SMT-LIB compliant expression.
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.
even if the API is undocumented
It actually is: https://ocamlpro.github.io/alt-ergo/Input_file_formats/Native/02_types/02_01_builtin.html#comparisons
Please remove the cleanups that have no place in a hotfix and it should be good.
src/lib/structures/expr.ml
Outdated
(* This fix makes sure that the smart constructor agrees with | ||
the SMT-LIB specification when used with at least 3 arguments. | ||
To prevent a soundness bug, we translate the expected expression into a | ||
conjonction of binary disequations. *) |
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.
Note: this is not specifically related to the SMT-LIB specification but to the usual definition of distinct
.
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.
Also the comment should point to #889 which will track the fix in the next
branch
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.
Actually I didn't plan to apply this patch on next but I can.
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.
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.
Ok :)
let args = Array.of_list args in | ||
let acc = ref vrai in | ||
for i = 0 to Array.length args - 1 do | ||
for j = i + 1 to Array.length args - 1 do | ||
acc := | ||
mk_and (neg (mk_eq ~iff args.(i) args.(j))) !acc false | ||
done; | ||
done; | ||
!acc |
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.
Nit: it doesn't really matter here as this is a hotfix but conversion to an array is not needed:
let distinct_from x acc ys =
List.fold_left (fun acc y -> mk_and (neg (mk_eq ~iff x y)) acc) acc ys
in let rec distinct_aux acc = function
| [] -> acc
| x :: ys -> distinct_aux (distinct_from x acc ys) ys
in distinct_aux acc args
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 know but I think the imperative version is the easier to read and a bit less error-prone. It's a matter of taste.
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 not only a matter of taste (I also find the imperative version easier to read in this specific case); the imperative version is slower and uses more memory because of the call to Array.of_list
. It is entirely negligible here (hence why it's a nit), just pointing that out.
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.
Sure ;)
src/lib/frontend/cnf.ml
Outdated
| TAneq lt -> | ||
let lt = List.map (make_term up_qv name_base) lt in | ||
E.mk_distinct ~iff:true lt | ||
|
||
| TAdistinct lt -> | ||
E.mk_distinct ~iff:true (List.map (make_term up_qv name_base) lt) | ||
|
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.
Can we avoid these changes? They don't seem useful, and keeping the patch as tiny as possible makes it easier to cherry-pick and move around if needed.
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.
Sure. Besides, we should use a different constructor for TAneq? It's not documented and I'm not sure what is the expected semantic for this constructor.
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.
TAneq [x; y]
is the constructor used for x <> y
. "neq" is a fairly common shorthand for "not equal".
src/lib/frontend/d_cnf.ml
Outdated
@@ -1337,7 +1337,7 @@ let rec mk_expr | |||
end | |||
|
|||
| B.Distinct, _ -> | |||
E.mk_distinct ~iff:true (List.map (fun t -> aux_mk_expr t) args) | |||
E.mk_distinct ~iff:true (List.map aux_mk_expr args) |
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.
Same as above, can we undo this? There shouldn't be this sort of unrelated cleanup in a hotfix.
634f3c9
to
2e9667e
Compare
src/lib/frontend/cnf.ml
Outdated
@@ -242,6 +242,7 @@ and make_form up_qv name_base ~toplevel f loc ~decl_kind : E.t = | |||
| TAneq lt | TAdistinct lt -> | |||
let lt = List.map (make_term up_qv name_base) lt in | |||
E.mk_distinct ~iff:true lt | |||
|
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.
Can you undo this whitespace change? Thanks.
2e9667e
to
af20710
Compare
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.
LGTM!
EDIT: I still had the notification to review the PR after it was merged
:D |
This PR quickly fixes the clash between the SMT-LIB specification of
distinct
and the implementation of this expression in AE.Basically, we expand the expression
distinct x_1 ... x_n
into a conjunction of binary disequalities.Notice that the model outputted for
uf2.models.smt2
changes because I don't preserve the old order used indistinct
.Fix issue #889 for the
v2.5.x
branch.