[sledge] Classify fully-interpreted and simplified exps differently

Summary:
For some Exp forms, Exp.solve is not complete, and this is necessary
since the result of solve is a substitution that needs to encode the
input equation as a conjunction of equations each between a variable
and an exp. This is tantamount to, stronger even than, the theory
being convex. So Exp.solve is not complete for some exps, and some of
those have constructors that perform some simplification. For example,
`(1 ≠ 0)` simplifies to `-1` (i.e. true). To enable deductions such as
`((x ≠ 0) = b) && x = 1 |- b = -1` needs the equality solver to
substitute through subexps of simplifiable exps like = and ≠, as it
does for interpreted exps like + and ×. At the same time, since
Exp.solve for non-interpreted exps cannot be complete, to enable
deductions such as `((x ≠ 0) = (y ≠ 0)) && x = 1 |- y ≠ 0` needs the
equality solver to congruence-close over subexps of simplifiable exps
such as = and ≠, as it does for uninterpreted exps.

To strengthen the equality solver in these sorts of cases, this diff
adds a new class of exps for = and ≠, and revises the equality solver
to handle them in a hybrid fashion between interpreted and
uninterpreted.

I am not currently sure whether or not this breaks the termination
proof, but I have also not managed to adapt usual divergent cases to
break this. One notable point is that simplifying = and ≠ exps always
produces genuinely simpler and smaller exps, in contrast to
e.g. polynomial simplification and gaussian elimination.

Note that the real solution to this problem is likely to be to
eliminate the i1 type in favor or a genuine boolean type, and
translate all integer operations on i1 to boolean/logical ones. Then
the disjunction implicit in e.g. equations between disequations would
appear as actual disjunction, and could be dealt with as such.

Reviewed By: jvillard

Differential Revision: D15424823

fbshipit-source-id: 67d62df1f
master
Josh Berdine 6 years ago committed by Facebook Github Bot
parent c690416622
commit 0cbcb878f9

@ -1319,7 +1319,8 @@ let rec is_constant e =
| _ -> true
let classify = function
| Add _ | Mul _ | App {op= Eq | Dq | App {op= Eq | Dq}} -> `Interpreted
| Add _ | Mul _ -> `Interpreted
| App {op= Eq | Dq | App {op= Eq | Dq}} -> `Simplified
| App _ -> `Uninterpreted
| _ -> `Atomic

@ -206,5 +206,5 @@ val fv : t -> Var.Set.t
val is_true : t -> bool
val is_false : t -> bool
val typ : t -> Typ.t option
val classify : t -> [> `Atomic | `Interpreted | `Uninterpreted]
val classify : t -> [> `Atomic | `Interpreted | `Simplified | `Uninterpreted]
val solve : t -> t -> (t, t, comparator_witness) Map.t option

@ -111,7 +111,8 @@ let apply s a = try Map.find_exn s a with Caml.Not_found -> a
let rec norm s a =
match Exp.classify a with
| `Interpreted -> Exp.map ~f:(norm s) a
| _ -> apply s a
| `Simplified -> apply s (Exp.map ~f:(norm s) a)
| `Atomic | `Uninterpreted -> apply s a
(** exps are congruent if equal after normalizing subexps *)
let congruent r a b =
@ -132,14 +133,14 @@ let lookup r a =
let rec canon r a =
match Exp.classify a with
| `Interpreted -> Exp.map ~f:(canon r) a
| `Uninterpreted -> lookup r (Exp.map ~f:(canon r) a)
| `Simplified | `Uninterpreted -> lookup r (Exp.map ~f:(canon r) a)
| `Atomic -> apply r.rep a
(** add an exp to the carrier *)
let rec extend a r =
match Exp.classify a with
| `Interpreted -> Exp.fold ~f:extend a ~init:r
| _ ->
| `Interpreted | `Simplified -> Exp.fold ~f:extend a ~init:r
| `Atomic | `Uninterpreted ->
Map.find_or_add r.rep a
~if_found:(fun _ -> r)
~default:a
@ -154,7 +155,7 @@ let compose r s =
if Exp.equal v1 v2 then v1
else fail "domains intersect: %a" Exp.pp key () )
in
{r with rep} |> check invariant
{r with rep}
let merge a b r =
[%Trace.call fun {pf} -> pf "%a@ %a@ %a" Exp.pp a Exp.pp b pp r]
@ -187,6 +188,15 @@ let rec close r =
| Some (a', b') -> close (merge a' b' r)
| None -> r
let close r =
[%Trace.call fun {pf} -> pf "%a" pp r]
;
close r
|>
[%Trace.retn fun {pf} r' ->
pf "%a" pp_diff (r, r') ;
invariant r']
let and_eq a b r =
if not r.sat then r
else
@ -266,7 +276,9 @@ let map_exps ({sat= _; rep} as r) ~f =
in
(if rep' == rep then r else {r with rep= rep'})
|>
[%Trace.retn fun {pf} r -> pf "%a" pp r ; invariant r]
[%Trace.retn fun {pf} r' ->
pf "%a" pp_diff (r, r') ;
invariant r']
let rename r sub = map_exps r ~f:(fun e -> Exp.rename e sub)

@ -9,9 +9,9 @@ let%test_module _ =
( module struct
open Equality
(* let () = Trace.init ~margin:160 ~config:all () *)
let () = Trace.init ~margin:68 ~config:none ()
(* let () = Trace.init ~margin:160 ~config:all () *)
let printf pp = Format.printf "@\n%a@." pp
let pp = printf pp
let pp_classes = printf pp_classes
@ -327,4 +327,67 @@ let%test_module _ =
let r12 = of_eqs [(!16, z - x); (x + !8 - z, z + !16 + !8 - z)]
let%expect_test _ = pp_classes r12 ; [%expect {| (%z_7 + -16) = %x_5 |}]
let r13 = of_eqs [(Exp.eq x !2, y); (Exp.dq x !2, z); (y, z)]
let%expect_test _ =
pp r13 ;
[%expect
{|
{sat= true;
rep= [[%x_5 ];
[%y_6 ];
[%z_7 %y_6];
[(%x_5 = 2) %y_6];
[(%x_5 2) %y_6];
[= ];
[ ];
[2 ]]} |}]
let%test _ = not (is_false r13) (* incomplete *)
let a = Exp.dq x !0
let r14 = of_eqs [(a, a); (x, !1)]
let%expect_test _ =
pp r14 ;
[%expect
{| {sat= true; rep= [[%x_5 1]; [ ]; [0 ]; [1 ]]} |}]
let%test _ = entails_eq r14 a (Exp.bool true)
let b = Exp.dq y !0
let r14 = of_eqs [(a, b); (x, !1)]
let%expect_test _ =
pp r14 ;
[%expect
{|
{sat= true;
rep= [[%x_5 1];
[%y_6 ];
[(%y_6 0) -1];
[ ];
[0 ];
[1 ]]} |}]
let%test _ = entails_eq r14 a (Exp.bool true)
let%test _ = entails_eq r14 b (Exp.bool true)
let b = Exp.convert ~dst:i64 ~src:i8 (Exp.dq x !0)
let r15 = of_eqs [(b, b); (x, !1)]
let%expect_test _ =
pp r15 ;
[%expect
{|
{sat= true;
rep= [[%x_5 1];
[((i64)(i8) (%x_5 0)) 1];
[ ];
[(i64)(i8) ];
[0 ];
[1 ]]} |}]
let%test _ = entails_eq r15 b !1
end )

Loading…
Cancel
Save