[sledge] Optimize Context.dnf by iterating disjuncts lazily

Summary:
This diff replaces Fml.fold_dnf, which eagerly enumerates a
disjunctive-normal form expansion, with iter_dnf which can be iterated
lazily. Fml.iter_dnf is then used in Context.dnf. This means that the
full DNF expansion does not need to be allocated by
e.g. Smtlib.check_sat, which yields huge peak memory savings.

Reviewed By: jvillard

Differential Revision: D25946111

fbshipit-source-id: e6d179e9f
master
Josh Berdine 4 years ago committed by Facebook GitHub Bot
parent 67ce2de306
commit 8baec586f0

@ -862,10 +862,8 @@ let dnf f =
let vs, x = add vs a x in let vs, x = add vs a x in
(vs, Fml.and_ p a, x) (vs, Fml.and_ p a, x)
in in
let join1 = Iter.cons in
let top = (Var.Set.empty, Fml.tt, empty) in let top = (Var.Set.empty, Fml.tt, empty) in
let bot = Iter.empty in Iter.from_labelled_iter (Fml.iter_dnf ~meet1 ~top f)
Fml.fold_dnf ~meet1 ~join1 ~top ~bot f
let rename x sub = let rename x sub =
[%trace] [%trace]

@ -174,7 +174,12 @@ let fold_pos_neg ~pos ~neg s ~f =
let f_not p s = f (not_ p) s in let f_not p s = f (not_ p) s in
Set.fold ~f:f_not neg (Set.fold ~f pos s) Set.fold ~f:f_not neg (Set.fold ~f pos s)
let fold_dnf ~meet1 ~join1 ~top ~bot fml = let iter_pos_neg ~pos ~neg ~f =
let f_not p = f (not_ p) in
Set.iter ~f pos ;
Set.iter ~f:f_not neg
let iter_dnf ~meet1 ~top fml ~f =
let rec add_conjunct fml (cjn, splits) = let rec add_conjunct fml (cjn, splits) =
match fml with match fml with
| Tt | Eq _ | Eq0 _ | Pos _ | Iff _ | Lit _ | Not _ -> | Tt | Eq _ | Eq0 _ | Pos _ | Iff _ | Lit _ | Not _ ->
@ -184,13 +189,13 @@ let fold_dnf ~meet1 ~join1 ~top ~bot fml =
| Cond {cnd; pos; neg} -> | Cond {cnd; pos; neg} ->
add_conjunct (or_ (and_ cnd pos) (and_ (not_ cnd) neg)) (cjn, splits) add_conjunct (or_ (and_ cnd pos) (and_ (not_ cnd) neg)) (cjn, splits)
in in
let rec add_disjunct (cjn, splits) fml djn = let rec add_disjunct (cjn, splits) fml =
let cjn, splits = add_conjunct fml (cjn, splits) in let cjn, splits = add_conjunct fml (cjn, splits) in
match splits with match splits with
| (pos, neg) :: splits -> | (pos, neg) :: splits ->
fold_pos_neg ~f:(add_disjunct (cjn, splits)) ~pos ~neg djn iter_pos_neg ~f:(add_disjunct (cjn, splits)) ~pos ~neg
| [] -> join1 cjn djn | [] -> f cjn
in in
add_disjunct (top, []) fml bot add_disjunct (top, []) fml
let vars p = Iter.flat_map ~f:Trm.vars (trms p) let vars p = Iter.flat_map ~f:Trm.vars (trms p)

@ -74,13 +74,8 @@ val map_pos_neg :
val fold_pos_neg : pos:set -> neg:set -> 'a -> f:(t -> 'a -> 'a) -> 'a val fold_pos_neg : pos:set -> neg:set -> 'a -> f:(t -> 'a -> 'a) -> 'a
val fold_dnf : val iter_dnf :
meet1:(t -> 'conjunction -> 'conjunction) meet1:(t -> 'a -> 'a) -> top:'a -> t -> f:('a -> unit) -> unit
-> join1:('conjunction -> 'disjunction -> 'disjunction)
-> top:'conjunction
-> bot:'disjunction
-> t
-> 'disjunction
val vars : t -> Var.t iter val vars : t -> Var.t iter
val trms : t -> Trm.t iter val trms : t -> Trm.t iter

@ -236,19 +236,19 @@ let%test_module _ =
{| {|
( infer_frame: 12 ( infer_frame: 12
%l_6 -[ %l_6, 16 )-> 8×%n_9,%a_2^(16 + -8×%n_9),%a_3 %l_6 -[ %l_6, 16 )-> 8×%n_9,%a_2^(16 + -8×%n_9),%a_3
* ( ( 0 = %n_9 emp) * ( ( 1 = %n_9 emp)
( 1 = %n_9 emp) ( 0 = %n_9 emp)
( 2 = %n_9 emp) ( 2 = %n_9 emp)
) )
\- %a_1, %m_8 . \- %a_1, %m_8 .
%l_6 -[ %l_6, %m_8 )-> %m_8,%a_1 %l_6 -[ %l_6, %m_8 )-> %m_8,%a_1
) infer_frame: ) infer_frame:
( ( 0 = %n_9 16 = %m_8 (0,%a_2^16,%a_3) = %a_1 emp) ( ( 1 = %n_9 16 = %m_8 (8,%a_2^8,%a_3) = %a_1 emp)
( %a_1 = %a_2 ( %a_1 = %a_2
2 = %n_9 2 = %n_9
16 = %m_8 16 = %m_8
(16 + %l_6) -[ %l_6, 16 )-> 0,%a_3) (16 + %l_6) -[ %l_6, 16 )-> 0,%a_3)
( 1 = %n_9 16 = %m_8 (8,%a_2^8,%a_3) = %a_1 emp) ( 0 = %n_9 16 = %m_8 (0,%a_2^16,%a_3) = %a_1 emp)
) |}] ) |}]
(* Incompleteness: equivalent to above but using ≤ instead of *) (* Incompleteness: equivalent to above but using ≤ instead of *)

Loading…
Cancel
Save