Skip to content
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

Merged
merged 3 commits into from
Oct 16, 2023
Merged

Conversation

Halbaroth
Copy link
Collaborator

@Halbaroth Halbaroth commented Oct 13, 2023

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.

Fix issue #889 for the v2.5.x branch.

@Halbaroth Halbaroth added this to the 2.5.2 milestone Oct 13, 2023
@Halbaroth Halbaroth force-pushed the distinct-soundness branch 2 times, most recently from 83e7d92 to e81c18a Compare October 13, 2023 15:36
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.
@bclement-ocp
Copy link
Collaborator

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. mk_distinct is supposed to build a distinct term; even if the API is undocumented, the intent (as expressed by the name mk_distinct) is clear to any reasonable user. mk_distinct not building a an "all-distinct" term is the bug that should be fixed.

(One other way to think about it: if we have a function mk_plus that actually created a multiplication, we would fix it in the function itself, we would not add workarounds)

@Halbaroth
Copy link
Collaborator Author

Besides, I checked that the constructor TANeq of the legacy parser is never used with a list of at least three elements.

The smart construct `mk_distinct` produces an SMT-LIB compliant
expression.
Copy link
Collaborator

@bclement-ocp bclement-ocp left a 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.

Comment on lines 1214 to 1217
(* 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. *)
Copy link
Collaborator

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.

Copy link
Collaborator

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

Copy link
Collaborator Author

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.

Copy link
Collaborator

@bclement-ocp bclement-ocp Oct 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I maybe was not clear. #889 is not about this patch, #889 is about the issue with distinct. We don't need to apply the patch on next. We will fix the issue with distinct on next in a different way, which is fine, but we should still refer to it here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok :)

Comment on lines +1218 to +1226
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
Copy link
Collaborator

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

Copy link
Collaborator Author

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.

Copy link
Collaborator

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure ;)

Comment on lines 242 to 248
| 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)

Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator

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".

@@ -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)
Copy link
Collaborator

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.

@@ -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

Copy link
Collaborator

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.

@Halbaroth Halbaroth merged commit 64ad841 into OCamlPro:v2.5.x Oct 16, 2023
10 checks passed
Copy link
Collaborator

@Stevendeo Stevendeo left a 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

@Halbaroth
Copy link
Collaborator Author

:D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants