Adding documentation comments in Internal

This commit is contained in:
Guillaume Bury 2016-07-01 12:07:49 +02:00
parent 34b1cda760
commit 66740e5de8
2 changed files with 144 additions and 62 deletions

2
_tags
View file

@ -2,7 +2,7 @@
true: color(always) true: color(always)
# optimization options # optimization options
true: optimize(3), unbox_closures true: optimize(3), unbox_closures, unbox_closures_factor(20)
# Include paths # Include paths
<sat>: include <sat>: include

View file

@ -23,11 +23,11 @@ module Make
(* a push/pop state *) (* a push/pop state *)
type user_level = { type user_level = {
(* User levels always refer to decision_level 0 *) (* User levels always refer to decision_level 0 *)
ul_elt_lvl : int; (* Number of atoms in trail at decision level 0 *) ul_elt_lvl : int; (* Number of atoms in trail at decision level 0 *)
ul_th_lvl : int; (* Number of atoms known by the theory at decicion level 0 *) ul_th_lvl : int; (* Number of atoms known by the theory at decicion level 0 *)
ul_th_env : Th.level; (* Theory state at level 0 *) ul_th_env : Th.level; (* Theory state at level 0 *)
ul_clauses : int; (* number of clauses *) ul_clauses : int; (* number of clauses *)
ul_learnt : int; (* number of learnt clauses *) ul_learnt : int; (* number of learnt clauses *)
} }
(* Singleton type containing the current state *) (* Singleton type containing the current state *)
@ -107,6 +107,7 @@ module Make
mutable nb_init_clauses : int; mutable nb_init_clauses : int;
} }
(* Starting environment. *)
let env = { let env = {
unsat_conflict = None; unsat_conflict = None;
next_decision = None; next_decision = None;
@ -157,18 +158,40 @@ module Make
nb_init_clauses = 0; nb_init_clauses = 0;
} }
(* Misc functions *)
let to_float i = float_of_int i
let to_int f = int_of_float f
let nb_clauses () = Vec.size env.clauses_hyps
let nb_vars () = St.nb_elt ()
let decision_level () = Vec.size env.elt_levels
let f_weight i j =
get_elt_weight (St.get_elt j) < get_elt_weight (St.get_elt i)
(* Is the assumptions currently unsat ? *)
let is_unsat () = let is_unsat () =
match env.unsat_conflict with match env.unsat_conflict with
| Some _ -> true | Some _ -> true
| None -> false | None -> false
(* Level for push/pop operations *)
type level = int
(* Push/Pop *) (* Push/Pop *)
let current_level () = Vec.size env.user_levels let current_level () = Vec.size env.user_levels
let push () = let push () : level =
if is_unsat () then current_level () if is_unsat () then
(* When unsat, pushing does nothing, since adding more assumptions
can not make the proof disappear. *)
current_level ()
else begin else begin
(* The assumptions are sat, or at least not yet detected unsat,
we need to save enough to be able to restore the current decision
level 0. *)
let res = current_level () in let res = current_level () in
(* To restore decision level 0, we need the stolver queue, and theory state. *)
let ul_elt_lvl, ul_th_lvl = let ul_elt_lvl, ul_th_lvl =
if Vec.is_empty env.elt_levels then if Vec.is_empty env.elt_levels then
env.elt_head, env.th_head env.elt_head, env.th_head
@ -179,58 +202,61 @@ module Make
if Vec.is_empty env.th_levels then Th.current_level () if Vec.is_empty env.th_levels then Th.current_level ()
else Vec.get env.th_levels 0 else Vec.get env.th_levels 0
in in
(* Keep in mind what are the current assumptions. *)
let ul_clauses = Vec.size env.clauses_hyps in let ul_clauses = Vec.size env.clauses_hyps in
let ul_learnt = Vec.size env.clauses_learnt in let ul_learnt = Vec.size env.clauses_learnt in
Vec.push env.user_levels {ul_elt_lvl; ul_th_lvl; ul_th_env; ul_clauses; ul_learnt;}; Vec.push env.user_levels {ul_elt_lvl; ul_th_lvl; ul_th_env; ul_clauses; ul_learnt;};
res res
end end
(* Level for push/pop operations *) (* To store info for level 0, it is easier to push at module
type level = int initialisation, when there are no assumptions. *)
let base_level = let base_level =
let l = push () in let l = push () in
assert (l = 0); assert (l = 0);
l l
(* Iteration over subterms *) (* Iteration over subterms.
module Mi = Map.Make(struct type t = int let compare = Pervasives.compare end) When incrementing activity, we want to be able to iterate over
let iter_map = ref Mi.empty all subterms of a formula. However, the function provided by the theory
may be costly (if it walks a tree-like structure, and does some processing
to ignore some subterms for instance), so we want to 'cache' to list
of subterms of each formula. To do so we use a hashtable from variable id to
list of subterms. *)
let iter_map = Hashtbl.create 1003
let iter_sub f v = let iter_sub f v =
try try
List.iter f (Mi.find v.vid !iter_map) List.iter f (Hashtbl.find iter_map v.vid)
with Not_found -> with Not_found ->
let l = ref [] in let l = ref [] in
Th.iter_assignable (fun t -> l := add_term t :: !l) v.pa.lit; Th.iter_assignable (fun t -> l := add_term t :: !l) v.pa.lit;
iter_map := Mi.add v.vid !l !iter_map; Hashtbl.add iter_map v.vid !l;
List.iter f !l List.iter f !l
let atom lit = (* When we have a new litteral,
we need to first create the list of its subterms. *)
let atom lit : atom =
let res = add_atom lit in let res = add_atom lit in
iter_sub ignore res.var; iter_sub ignore res.var;
res res
(* Misc functions *) (* Variable and litteral activity.
let to_float i = float_of_int i Activity is used to decide on which variable to decide when propagation
let to_int f = int_of_float f is done. Uses a heap (implemented in Iheap), to keep track of variable activity.
To be more general, the heap only stores the variable/litteral id (i.e an int).
(* Accessors for litterals *) When we add a variable (which wraps a formula), we also need to add all
let f_weight i j = its subterms.
get_elt_weight (St.get_elt j) < get_elt_weight (St.get_elt i) *)
(*
let f_filter i =
get_elt_level (St.get_elt i) < 0
*)
(* Var/clause activity *)
let insert_var_order = function let insert_var_order = function
| Either.Left l -> Iheap.insert f_weight env.order l.lid | Either.Left l -> Iheap.insert f_weight env.order l.lid
| Either.Right v -> | Either.Right v ->
Iheap.insert f_weight env.order v.vid; Iheap.insert f_weight env.order v.vid;
iter_sub (fun t -> Iheap.insert f_weight env.order t.lid) v iter_sub (fun t -> Iheap.insert f_weight env.order t.lid) v
(* Rather than iterate over all the heap when we want to decrease all the
variables/literals activity, we instead increase the value by which
we increase the activity of 'interesting' var/lits. *)
let var_decay_activity () = let var_decay_activity () =
env.var_incr <- env.var_incr *. env.var_decay env.var_incr <- env.var_incr *. env.var_decay
@ -273,33 +299,45 @@ module Make
env.clause_incr <- env.clause_incr *. 1e-20 env.clause_incr <- env.clause_incr *. 1e-20
end end
(* Convenient access *) (* Simplification of clauses.
let decision_level () = Vec.size env.elt_levels When adding new clauses, it is desirable to 'simplify' them, i.e:
- remove variables that are false at level 0, since it is a fact
that they cannot be true, and therefore can not help to satisfy the clause
let nb_clauses () = Vec.size env.clauses_hyps Aditionally, since we can do push/pop on the assumptions, we need to
let nb_vars () = St.nb_elt () keep track of what assumptions were used to simplify a given clause.
*)
(* Simplify clauses *)
exception Trivial exception Trivial
let simplify_zero atoms level0 = let simplify_zero atoms level0 =
(* Eliminates dead litterals from clauses when at decision level 0 *) (* Eliminates dead litterals from clauses when at decision level 0 (see above) *)
assert (decision_level () = 0); assert (decision_level () = 0);
let aux (atoms, history, lvl) a = let aux (atoms, history, lvl) a =
if a.is_true then raise Trivial; if a.is_true then raise Trivial;
(* If a variable is true at level 0, then the clause is always satisfied *)
if a.neg.is_true then begin if a.neg.is_true then begin
(* If a variable is false, we need to see why it is false. *)
match a.var.reason with match a.var.reason with
| None | Some Decision -> assert false | None | Some Decision -> assert false
(* The var must have a reason, and it cannot be a decision, since we are
at level 0. *)
| Some (Bcp cl) -> atoms, cl :: history, max lvl cl.c_level | Some (Bcp cl) -> atoms, cl :: history, max lvl cl.c_level
(* The variable has been set to false because of another clause,
we then need to keep track of the assumption level used. *)
| Some (Semantic 0) -> atoms, history, lvl | Some (Semantic 0) -> atoms, history, lvl
(* Semantic propagations at level 0 are, well not easy to deal with,
this shouldn't really happen actually (because semantic propagations
at level 0 currently lack a proof). *)
| Some (Semantic _) -> | Some (Semantic _) ->
Log.debugf 0 "Unexpected semantic propagation at level 0: %a" Log.debugf 0 "Unexpected semantic propagation at level 0: %a"
(fun k->k St.pp_atom a); (fun k->k St.pp_atom a);
assert false assert false
end else end else
a::atoms, history, lvl a::atoms, history, lvl
(* General case, we do not know the truth value of a, just let it be. *)
in in
let atoms, init, lvl = List.fold_left aux ([], [], level0) atoms in let atoms, init, lvl = List.fold_left aux ([], [], level0) atoms in
(* TODO: Why do we sort the atoms here ? *)
List.fast_sort (fun a b -> a.var.vid - b.var.vid) atoms, init, lvl List.fast_sort (fun a b -> a.var.vid - b.var.vid) atoms, init, lvl
let partition atoms init0 = let partition atoms init0 =
@ -309,12 +347,17 @@ module Make
| a :: r -> | a :: r ->
if a.is_true then if a.is_true then
if a.var.v_level = 0 then raise Trivial if a.var.v_level = 0 then raise Trivial
(* Same as before, a var true at level 0 gives a trivially true clause *)
else (a::trues) @ unassigned @ falses @ r, history, lvl else (a::trues) @ unassigned @ falses @ r, history, lvl
(* A var true at level > 0 does not change anything, but is unlikely
to be watched, so we put prefer to put them at the end. *)
else if a.neg.is_true then else if a.neg.is_true then
if a.var.v_level = 0 then begin if a.var.v_level = 0 then begin
match a.var.reason with match a.var.reason with
| Some (Bcp cl) -> | Some (Bcp cl) ->
partition_aux trues unassigned falses (cl :: history) (max lvl cl.c_level) r partition_aux trues unassigned falses (cl :: history) (max lvl cl.c_level) r
(* Same as before, a var false at level 0 can be eliminated from the clause,
but we need to kepp in mind that we used another clause to simplify it. *)
| Some (Semantic 0) -> | Some (Semantic 0) ->
partition_aux trues unassigned falses history lvl r partition_aux trues unassigned falses history lvl r
| _ -> assert false | _ -> assert false
@ -328,7 +371,8 @@ module Make
else else
partition_aux [] [] [] [] init0 atoms partition_aux [] [] [] [] init0 atoms
(* Compute a progess estimate *) (* Compute a progess estimate.
TODO: remove it or use it ? *)
let progress_estimate () = let progress_estimate () =
let prg = ref 0. in let prg = ref 0. in
let nbv = to_float (nb_vars()) in let nbv = to_float (nb_vars()) in
@ -342,7 +386,13 @@ module Make
!prg /. nbv !prg /. nbv
(* Manipulating decision levels *) (* Making a decision.
Before actually creatig a new decision level, we check that
all propagations have been done and propagated to the theory,
i.e that the theoriy state indeed takes into account the whole
stack of litterals
i.e we have indeed reached a propagation fixpoint before making
a new decision *)
let new_decision_level() = let new_decision_level() =
assert (env.th_head = Vec.size env.elt_queue); assert (env.th_head = Vec.size env.elt_queue);
assert (env.elt_head = Vec.size env.elt_queue); assert (env.elt_head = Vec.size env.elt_queue);
@ -350,10 +400,14 @@ module Make
Vec.push env.th_levels (Th.current_level ()); (* save the current tenv *) Vec.push env.th_levels (Th.current_level ()); (* save the current tenv *)
() ()
(* Adding/removing clauses *) (* Attach/Detach a clause.
Clauses that become satisfied are detached, i.e we remove
their watchers, while clauses that loose their satisfied status
have to be reattached, adding watchers. *)
let attach_clause c = let attach_clause c =
Vec.push (Vec.get c.atoms 0).neg.watched c; Vec.push (Vec.get c.atoms 0).neg.watched c;
Vec.push (Vec.get c.atoms 1).neg.watched c; Vec.push (Vec.get c.atoms 1).neg.watched c;
(* TODO: Learnt litterals are not really used anymre, :p *)
if c.learnt then if c.learnt then
env.learnts_literals <- env.learnts_literals + Vec.size c.atoms env.learnts_literals <- env.learnts_literals + Vec.size c.atoms
else else
@ -362,36 +416,46 @@ module Make
let detach_clause c = let detach_clause c =
Log.debugf 10 "Removing clause @[%a@]" (fun k->k St.pp_clause c); Log.debugf 10 "Removing clause @[%a@]" (fun k->k St.pp_clause c);
c.removed <- true; c.removed <- true;
(* Not necessary, cleanup is done during propagation
Vec.remove (Vec.get c.atoms 0).neg.watched c;
Vec.remove (Vec.get c.atoms 1).neg.watched c;
*)
if c.learnt then if c.learnt then
env.learnts_literals <- env.learnts_literals - Vec.size c.atoms env.learnts_literals <- env.learnts_literals - Vec.size c.atoms
else else
env.clauses_literals <- env.clauses_literals - Vec.size c.atoms env.clauses_literals <- env.clauses_literals - Vec.size c.atoms
let remove_clause c = detach_clause c (* Is a clause satisfied ? *)
let satisfied c = Vec.exists (fun atom -> atom.is_true) c.atoms
let satisfied c = (* Backtracking.
Vec.exists (fun atom -> atom.is_true) c.atoms Used to backtrack, i.e cancel down to [lvl] excluded,
i.e we want to go back to the state the solver was in
(* cancel down to [lvl] excluded *) when decision level [lvl] was created. *)
let cancel_until lvl = let cancel_until lvl =
(* Nothing to do if we try to backtrack to a non-existent level. *)
if decision_level () > lvl then begin if decision_level () > lvl then begin
(* We set the head of the solver and theory queue to what it was. *)
env.elt_head <- Vec.get env.elt_levels lvl; env.elt_head <- Vec.get env.elt_levels lvl;
env.th_head <- env.elt_head; env.th_head <- env.elt_head;
(* Now we need to cleanup the vars that are not valid anymore
(i.e to the right of elt_head in the queue. *)
for c = env.elt_head to Vec.size env.elt_queue - 1 do for c = env.elt_head to Vec.size env.elt_queue - 1 do
match (Vec.get env.elt_queue c) with match (Vec.get env.elt_queue c) with
(* A literal is unassigned, we nedd to add it back to
the heap of potentially assignable literals. *)
| Either.Left l -> | Either.Left l ->
l.assigned <- None; l.assigned <- None;
l.l_level <- -1; l.l_level <- -1;
insert_var_order (elt_of_lit l) insert_var_order (elt_of_lit l)
(* A variable is not true/false anymore, one of two things can happen: *)
| Either.Right a -> | Either.Right a ->
if a.var.v_level <= lvl then begin if a.var.v_level <= lvl then begin
(* It is a semantic propagation, which can be late, and has a level
lower than where we backtrack, so we just move it to the head
of the queue. *)
Vec.set env.elt_queue env.elt_head (of_atom a); Vec.set env.elt_queue env.elt_head (of_atom a);
env.elt_head <- env.elt_head + 1 env.elt_head <- env.elt_head + 1
end else begin end else begin
(* it is a result of bolean propagation, or a semantic propagation
with a level higher than the level to which we backtrack,
in that case, we simply unset its value and reinsert it into the heap. *)
a.is_true <- false; a.is_true <- false;
a.neg.is_true <- false; a.neg.is_true <- false;
a.var.v_level <- -1; a.var.v_level <- -1;
@ -399,7 +463,9 @@ module Make
insert_var_order (elt_of_var a.var) insert_var_order (elt_of_var a.var)
end end
done; done;
Th.backtrack (Vec.get env.th_levels lvl); (* recover the right tenv *) (* Recover the right theory state. *)
Th.backtrack (Vec.get env.th_levels lvl);
(* Resize the vectors according to their new size. *)
Vec.shrink env.elt_queue ((Vec.size env.elt_queue) - env.elt_head); Vec.shrink env.elt_queue ((Vec.size env.elt_queue) - env.elt_head);
Vec.shrink env.elt_levels ((Vec.size env.elt_levels) - lvl); Vec.shrink env.elt_levels ((Vec.size env.elt_levels) - lvl);
Vec.shrink env.th_levels ((Vec.size env.th_levels) - lvl); Vec.shrink env.th_levels ((Vec.size env.th_levels) - lvl);
@ -407,24 +473,40 @@ module Make
assert (Vec.size env.elt_levels = Vec.size env.th_levels); assert (Vec.size env.elt_levels = Vec.size env.th_levels);
() ()
(* Unsatisfiability is signaled through an exception, since it can happen
in multiple places (adding new clauses, or solving for instance). *)
let report_unsat ({atoms=atoms} as confl) = let report_unsat ({atoms=atoms} as confl) =
Log.debugf 5 "@[Unsat conflict: %a@]" (fun k -> k St.pp_clause confl); Log.debugf 5 "@[Unsat conflict: %a@]" (fun k -> k St.pp_clause confl);
env.unsat_conflict <- Some confl; env.unsat_conflict <- Some confl;
raise Unsat raise Unsat
(* Simplification of boolean propagation reasons.
When doing boolean propagation *at level 0*, it can happen
that the clause cl, which propagates a formula, also contains
other formulas, but has been simplified. in which case, we
need to rebuild a clause with correct history, in order to
be able to build a correct proof at the end of proof search. *)
let simpl_reason = function let simpl_reason = function
| (Bcp cl) as r -> | (Bcp cl) as r ->
let l, history, c_lvl = partition (Vec.to_list cl.atoms) 0 in let l, history, c_lvl = partition (Vec.to_list cl.atoms) 0 in
begin match l with begin match l with
| [ a ] -> | [ a ] ->
if history = [] then r if history = [] then r
(* no simplification has been done, so [cl] is actually a clause with only
[a], so it is a valid reason for propagating [a]. *)
else else
(* Clauses in [history] have been used to simplify [cl] into a clause [tmp_cl]
with only one formula (which is [a]). So we explicitly create that clause
and set it as the cause for the propagation of [a], that way we can
rebuild the whole resolution tree when we want to prove [a]. *)
let tmp_cl = make_clause (fresh_tname ()) l 1 true (History (cl :: history)) c_lvl in let tmp_cl = make_clause (fresh_tname ()) l 1 true (History (cl :: history)) c_lvl in
Bcp tmp_cl Bcp tmp_cl
| _ -> assert false | _ -> assert false
end end
| r -> r | r -> r
(* Boolean propagation.
Wrapper function for adding a new propagated formula. *)
let enqueue_bool a lvl reason = let enqueue_bool a lvl reason =
if a.neg.is_true then begin if a.neg.is_true then begin
Log.debugf 0 "Trying to enqueue a false litteral: %a" (fun k->k St.pp_atom a); Log.debugf 0 "Trying to enqueue a false litteral: %a" (fun k->k St.pp_atom a);
@ -610,7 +692,7 @@ module Make
clause_bump_activity lclause; clause_bump_activity lclause;
if is_uip then if is_uip then
enqueue_bool fuip blevel (Bcp lclause) enqueue_bool fuip blevel (Bcp lclause)
else begin else begin
env.next_decision <- Some fuip.neg env.next_decision <- Some fuip.neg
end end
end; end;
@ -790,10 +872,10 @@ module Make
let res = ref None in let res = ref None in
while env.elt_head < Vec.size env.elt_queue do while env.elt_head < Vec.size env.elt_queue do
begin match Vec.get env.elt_queue env.elt_head with begin match Vec.get env.elt_queue env.elt_head with
| Either.Left _ -> () | Either.Left _ -> ()
| Either.Right a -> | Either.Right a ->
incr num_props; incr num_props;
propagate_atom a res propagate_atom a res
end; end;
env.elt_head <- env.elt_head + 1; env.elt_head <- env.elt_head + 1;
done; done;
@ -838,14 +920,14 @@ module Make
for i = 0 to lim1 - 1 do for i = 0 to lim1 - 1 do
let c = Vec.get env.learnts i in let c = Vec.get env.learnts i in
if Vec.size c.atoms > 2 && not (locked c) then if Vec.size c.atoms > 2 && not (locked c) then
remove_clause c detach_clause c
else else
begin Vec.set env.learnts !j c; incr j end begin Vec.set env.learnts !j c; incr j end
done; done;
for i = lim1 to lim2 - 1 do for i = lim1 to lim2 - 1 do
let c = Vec.get env.learnts i in let c = Vec.get env.learnts i in
if Vec.size c.atoms > 2 && not (locked c) && c.activity < extra_lim then if Vec.size c.atoms > 2 && not (locked c) && c.activity < extra_lim then
remove_clause c detach_clause c
else else
begin Vec.set env.learnts !j c; incr j end begin Vec.set env.learnts !j c; incr j end
done; done;
@ -857,7 +939,7 @@ module Make
let remove_satisfied vec = let remove_satisfied vec =
for i = 0 to Vec.size vec - 1 do for i = 0 to Vec.size vec - 1 do
let c = Vec.get vec i in let c = Vec.get vec i in
if satisfied c then remove_clause c if satisfied c then detach_clause c
done done
let simplify () = let simplify () =
@ -961,8 +1043,8 @@ module Make
add_clause c; add_clause c;
(* Clauses can be added after search has begun (and thus we are not at level 0, (* Clauses can be added after search has begun (and thus we are not at level 0,
so better not do anything at this point. so better not do anything at this point.
match propagate () with match propagate () with
| None -> () | Some confl -> report_unsat confl | None -> () | Some confl -> report_unsat confl
*) *)
in in
List.iter aux cnf List.iter aux cnf
@ -1106,7 +1188,7 @@ module Make
for i = ul.ul_clauses to Vec.size env.clauses_hyps - 1 do for i = ul.ul_clauses to Vec.size env.clauses_hyps - 1 do
let c = Vec.get env.clauses_hyps i in let c = Vec.get env.clauses_hyps i in
assert (c.c_level > l); assert (c.c_level > l);
remove_clause c detach_clause c
done; done;
Vec.shrink env.clauses_hyps (Vec.size env.clauses_hyps - ul.ul_clauses); Vec.shrink env.clauses_hyps (Vec.size env.clauses_hyps - ul.ul_clauses);
@ -1116,7 +1198,7 @@ module Make
for i = ul.ul_learnt to Vec.size env.clauses_learnt - 1 do for i = ul.ul_learnt to Vec.size env.clauses_learnt - 1 do
let c = Vec.get env.clauses_learnt i in let c = Vec.get env.clauses_learnt i in
if c.c_level > l then begin if c.c_level > l then begin
remove_clause c; detach_clause c;
match c.cpremise with match c.cpremise with
| History ({ cpremise = Lemma _ } as c' :: _ ) -> Stack.push c' s | History ({ cpremise = Lemma _ } as c' :: _ ) -> Stack.push c' s
| _ -> () (* Only simplified clauses can have a level > 0 *) | _ -> () (* Only simplified clauses can have a level > 0 *)