mirror of
https://github.com/c-cube/sidekick.git
synced 2025-12-07 11:45:41 -05:00
324 lines
9.4 KiB
OCaml
324 lines
9.4 KiB
OCaml
(**************************************************************************)
|
|
(* *)
|
|
(* Alt-Ergo Zero *)
|
|
(* *)
|
|
(* Sylvain Conchon and Alain Mebsout *)
|
|
(* Universite Paris-Sud 11 *)
|
|
(* *)
|
|
(* Copyright 2011. This file is distributed under the terms of the *)
|
|
(* Apache Software License version 2.0 *)
|
|
(* *)
|
|
(**************************************************************************)
|
|
|
|
module type S = Tseitin_intf.S
|
|
|
|
module Make (F : Formula_intf.S) = struct
|
|
|
|
exception Empty_Or
|
|
type combinator = And | Or | Imp | Not
|
|
|
|
type atom = F.t
|
|
type t =
|
|
| True
|
|
| Lit of atom
|
|
| Comb of combinator * t list
|
|
|
|
let rec print fmt phi =
|
|
match phi with
|
|
| True -> Format.fprintf fmt "true"
|
|
| Lit a -> F.print fmt a
|
|
| Comb (Not, [f]) ->
|
|
Format.fprintf fmt "not (%a)" print f
|
|
| Comb (And, l) -> Format.fprintf fmt "(%a)" (print_list "and") l
|
|
| Comb (Or, l) -> Format.fprintf fmt "(%a)" (print_list "or") l
|
|
| Comb (Imp, [f1; f2]) ->
|
|
Format.fprintf fmt "(%a => %a)" print f1 print f2
|
|
| _ -> assert false
|
|
and print_list sep fmt = function
|
|
| [] -> ()
|
|
| [f] -> print fmt f
|
|
| f::l -> Format.fprintf fmt "%a %s %a" print f sep (print_list sep) l
|
|
|
|
let make comb l = Comb (comb, l)
|
|
|
|
let make_atom p = Lit p
|
|
|
|
let f_true = True
|
|
let f_false = Comb(Not, [True])
|
|
|
|
let rec flatten comb acc = function
|
|
| [] -> acc
|
|
| (Comb (c, l)) :: r when c = comb ->
|
|
flatten comb (List.rev_append l acc) r
|
|
| a :: r ->
|
|
flatten comb (a :: acc) r
|
|
|
|
let rec opt_rev_map f acc = function
|
|
| [] -> acc
|
|
| a :: r -> begin match f a with
|
|
| None -> opt_rev_map f acc r
|
|
| Some b -> opt_rev_map f (b :: acc) r
|
|
end
|
|
|
|
let remove_true l =
|
|
let aux = function
|
|
| True -> None
|
|
| f -> Some f
|
|
in
|
|
opt_rev_map aux [] l
|
|
|
|
let remove_false l =
|
|
let aux = function
|
|
| Comb(Not, [True]) -> None
|
|
| f -> Some f
|
|
in
|
|
opt_rev_map aux [] l
|
|
|
|
|
|
let make_not f = make Not [f]
|
|
|
|
let make_and l =
|
|
let l' = remove_true (flatten And [] l) in
|
|
if List.exists ((=) f_false) l' then
|
|
f_false
|
|
else
|
|
make And l'
|
|
|
|
let make_or l =
|
|
let l' = remove_false (flatten Or [] l) in
|
|
if List.exists ((=) f_true) l' then
|
|
f_true
|
|
else match l' with
|
|
| [] -> raise Empty_Or
|
|
| [a] -> a
|
|
| _ -> Comb (Or, l')
|
|
|
|
let make_imply f1 f2 = make Imp [f1; f2]
|
|
let make_equiv f1 f2 = make_and [ make_imply f1 f2; make_imply f2 f1]
|
|
let make_xor f1 f2 = make_or [ make_and [ make_not f1; f2 ];
|
|
make_and [ f1; make_not f2 ] ]
|
|
|
|
(* simplify formula *)
|
|
let (%%) f g x = f (g x)
|
|
|
|
let rec sform f k = match f with
|
|
| True | Comb (Not, [True]) -> k f
|
|
| Comb (Not, [Lit a]) -> k (Lit (F.neg a))
|
|
| Comb (Not, [Comb (Not, [f])]) -> sform f k
|
|
| Comb (Not, [Comb (Or, l)]) -> sform_list_not [] l (k %% make_and)
|
|
| Comb (Not, [Comb (And, l)]) -> sform_list_not [] l (k %% make_or)
|
|
| Comb (And, l) -> sform_list [] l (k %% make_and)
|
|
| Comb (Or, l) -> sform_list [] l (k %% make_or)
|
|
| Comb (Imp, [f1; f2]) ->
|
|
sform (make_not f1) (fun f1' -> sform f2 (fun f2' -> k (make_or [f1'; f2'])))
|
|
| Comb (Not, [Comb (Imp, [f1; f2])]) ->
|
|
sform f1 (fun f1' -> sform (make_not f2) (fun f2' -> k (make_and [f1';f2'])))
|
|
| Comb ((Imp | Not), _) -> assert false
|
|
| Lit _ -> k f
|
|
and sform_list acc l k = match l with
|
|
| [] -> k acc
|
|
| f :: tail ->
|
|
sform f (fun f' -> sform_list (f'::acc) tail k)
|
|
and sform_list_not acc l k = match l with
|
|
| [] -> k acc
|
|
| f :: tail ->
|
|
sform (make_not f) (fun f' -> sform_list_not (f'::acc) tail k)
|
|
|
|
let ( @@ ) l1 l2 = List.rev_append l1 l2
|
|
let ( @ ) = `Use_rev_append_instead (* prevent use of non-tailrec append *)
|
|
|
|
(*
|
|
let distrib l_and l_or =
|
|
let l =
|
|
if l_or = [] then l_and
|
|
else
|
|
List.rev_map
|
|
(fun x ->
|
|
match x with
|
|
| Lit _ -> Comb (Or, x::l_or)
|
|
| Comb (Or, l) -> Comb (Or, l @@ l_or)
|
|
| _ -> assert false
|
|
) l_and
|
|
in
|
|
Comb (And, l)
|
|
|
|
let rec flatten_or = function
|
|
| [] -> []
|
|
| Comb (Or, l)::r -> l @@ (flatten_or r)
|
|
| Lit a :: r -> (Lit a)::(flatten_or r)
|
|
| _ -> assert false
|
|
|
|
let rec flatten_and = function
|
|
| [] -> []
|
|
| Comb (And, l)::r -> l @@ (flatten_and r)
|
|
| a :: r -> a::(flatten_and r)
|
|
|
|
|
|
let rec cnf f =
|
|
match f with
|
|
| Comb (Or, l) ->
|
|
begin
|
|
let l = List.rev_map cnf l in
|
|
let l_and, l_or =
|
|
List.partition (function Comb(And,_) -> true | _ -> false) l in
|
|
match l_and with
|
|
| [ Comb(And, l_conj) ] ->
|
|
let u = flatten_or l_or in
|
|
distrib l_conj u
|
|
|
|
| Comb(And, l_conj) :: r ->
|
|
let u = flatten_or l_or in
|
|
cnf (Comb(Or, (distrib l_conj u)::r))
|
|
|
|
| _ ->
|
|
begin
|
|
match flatten_or l_or with
|
|
| [] -> assert false
|
|
| [r] -> r
|
|
| v -> Comb (Or, v)
|
|
end
|
|
end
|
|
| Comb (And, l) ->
|
|
Comb (And, List.rev_map cnf l)
|
|
| f -> f
|
|
|
|
let rec mk_cnf = function
|
|
| Comb (And, l) ->
|
|
List.fold_left (fun acc f -> (mk_cnf f) @@ acc) [] l
|
|
|
|
| Comb (Or, [f1;f2]) ->
|
|
let ll1 = mk_cnf f1 in
|
|
let ll2 = mk_cnf f2 in
|
|
List.fold_left
|
|
(fun acc l1 -> (List.rev_map (fun l2 -> l1 @@ l2)ll2) @@ acc) [] ll1
|
|
|
|
| Comb (Or, f1 :: l) ->
|
|
let ll1 = mk_cnf f1 in
|
|
let ll2 = mk_cnf (Comb (Or, l)) in
|
|
List.fold_left
|
|
(fun acc l1 -> (List.rev_map (fun l2 -> l1 @@ l2)ll2) @@ acc) [] ll1
|
|
|
|
| Lit a -> [[a]]
|
|
| Comb (Not, [Lit a]) -> [[F.neg a]]
|
|
| _ -> assert false
|
|
|
|
|
|
let rec unfold mono f =
|
|
match f with
|
|
| Lit a -> a::mono
|
|
| Comb (Not, [Lit a]) ->
|
|
(F.neg a)::mono
|
|
| Comb (Or, l) ->
|
|
List.fold_left unfold mono l
|
|
| _ -> assert false
|
|
|
|
let rec init monos f =
|
|
match f with
|
|
| Comb (And, l) ->
|
|
List.fold_left init monos l
|
|
| f -> (unfold [] f)::monos
|
|
|
|
let make_cnf f =
|
|
let sfnc = cnf (sform f) in
|
|
init [] sfnc
|
|
*)
|
|
|
|
let mk_proxy = F.fresh
|
|
|
|
let acc_or = ref []
|
|
let acc_and = ref []
|
|
|
|
(* build a clause by flattening (if sub-formulas have the
|
|
same combinator) and proxy-ing sub-formulas that have the
|
|
opposite operator. *)
|
|
let rec cnf f = match f with
|
|
| Lit a -> None, [a]
|
|
| Comb (Not, [Lit a]) -> None, [F.neg a]
|
|
|
|
| Comb (And, l) ->
|
|
List.fold_left
|
|
(fun (_, acc) f ->
|
|
match cnf f with
|
|
| _, [] -> assert false
|
|
| cmb, [a] -> Some And, a :: acc
|
|
| Some And, l ->
|
|
Some And, l @@ acc
|
|
(* let proxy = mk_proxy () in *)
|
|
(* acc_and := (proxy, l) :: !acc_and; *)
|
|
(* proxy :: acc *)
|
|
| Some Or, l ->
|
|
let proxy = mk_proxy () in
|
|
acc_or := (proxy, l) :: !acc_or;
|
|
Some And, proxy :: acc
|
|
| None, l -> Some And, l @@ acc
|
|
| _ -> assert false
|
|
) (None, []) l
|
|
|
|
| Comb (Or, l) ->
|
|
List.fold_left
|
|
(fun (_, acc) f ->
|
|
match cnf f with
|
|
| _, [] -> assert false
|
|
| cmb, [a] -> Some Or, a :: acc
|
|
| Some Or, l ->
|
|
Some Or, l @@ acc
|
|
(* let proxy = mk_proxy () in *)
|
|
(* acc_or := (proxy, l) :: !acc_or; *)
|
|
(* proxy :: acc *)
|
|
| Some And, l ->
|
|
let proxy = mk_proxy () in
|
|
acc_and := (proxy, l) :: !acc_and;
|
|
Some Or, proxy :: acc
|
|
| None, l -> Some Or, l @@ acc
|
|
| _ -> assert false
|
|
) (None, []) l
|
|
| _ -> assert false
|
|
|
|
let cnf f =
|
|
let acc = match f with
|
|
| True -> []
|
|
| Comb(Not, [True]) -> [[]]
|
|
| Comb (And, l) -> List.rev_map (fun f -> snd(cnf f)) l
|
|
| _ -> [snd (cnf f)]
|
|
in
|
|
let proxies = ref [] in
|
|
(* encore clauses that make proxies in !acc_and equivalent to
|
|
their clause *)
|
|
let acc =
|
|
List.fold_left
|
|
(fun acc (p,l) ->
|
|
proxies := p :: !proxies;
|
|
let np = F.neg p in
|
|
(* build clause [cl = l1 & l2 & ... & ln => p] where [l = [l1;l2;..]]
|
|
also add clauses [p => l1], [p => l2], etc. *)
|
|
let cl, acc =
|
|
List.fold_left
|
|
(fun (cl,acc) a -> (F.neg a :: cl), [np; a] :: acc)
|
|
([p],acc) l in
|
|
cl :: acc
|
|
) acc !acc_and
|
|
in
|
|
(* encore clauses that make proxies in !acc_or equivalent to
|
|
their clause *)
|
|
let acc =
|
|
List.fold_left
|
|
(fun acc (p,l) ->
|
|
proxies := p :: !proxies;
|
|
(* add clause [p => l1 | l2 | ... | ln], and add clauses
|
|
[l1 => p], [l2 => p], etc. *)
|
|
let acc = List.fold_left (fun acc a -> [p; F.neg a]::acc)
|
|
acc l in
|
|
(F.neg p :: l) :: acc
|
|
) acc !acc_or
|
|
in
|
|
acc
|
|
|
|
let make_cnf f =
|
|
acc_or := [];
|
|
acc_and := [];
|
|
cnf (sform f (fun f' -> f'))
|
|
|
|
(* Naive CNF XXX remove???
|
|
let make_cnf f = mk_cnf (sform f)
|
|
*)
|
|
end
|