From 3ca38cae1d249eee11e4a514ba1c852a5b30eae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bobot?= <francois@bobot.eu> Date: Thu, 7 Feb 2013 17:56:41 +0100 Subject: [PATCH] [Solver|Arith] Work but with many hacks problems with the daemon are registered not delayed but the effect are. They look at only one place --- src/arith.ml | 236 +++++++++++++++------- src/arith.mli | 18 +- src/inputlang/altergo/popop_of_altergo.ml | 69 +++---- src/solver.ml | 230 +++++++++++++++------ src/solver.mli | 23 ++- src/uninterp.ml | 18 +- tests/tests_lib.ml | 10 +- tests/tests_uf.ml | 4 +- 8 files changed, 411 insertions(+), 197 deletions(-) diff --git a/src/arith.ml b/src/arith.ml index cb7c436e8..59945643b 100644 --- a/src/arith.ml +++ b/src/arith.ml @@ -19,9 +19,8 @@ (* for more details (enclosed in the file licenses/LGPLv2.1). *) (* *) (**************************************************************************) - -module E = Egraph_simple -open Egraph_simple.MTerm +open Stdlib +open Solver let debug = Debug.register_info_flag ~desc:"for the arithmetic theory" @@ -29,6 +28,8 @@ let debug = Debug.register_info_flag type t = { cst : Q.t; poly : Q.t Cl.M.t} +let sem : t sem = create_sem_key () + let cst q = {cst = q; poly = Cl.M.empty} let monome c x = {cst = Q.zero; poly = Cl.M.singleton x c} @@ -68,11 +69,10 @@ let x_p_cy p1 c p2 = m1 m2 in {cst = f p1.cst p2.cst; poly = poly p1.poly p2.poly} -module Th = struct - type repr = t - - module Make(X:S with type repr := repr)= struct - let kind = Other +module T = struct + let key = sem + type r = t + type t = r let equal n m = Q.equal n.cst m.cst && Cl.M.equal Q.equal n.poly m.poly @@ -81,6 +81,18 @@ module Th = struct Cl.hash k * 101 + Hashtbl.hash v * 107 + acc * 253) n.poly (Hashtbl.hash n.cst * 27) + let compare n m = + let c = Q.compare n.cst m.cst in + if c <> 0 then c + else Cl.M.compare Q.compare n.poly m.poly +end + +module Th = struct + include T + module M = Map.Make(T) + module S = M.Set + module H = XHashtbl.Make(T) + let print print_cl fmt v = Format.fprintf fmt "@["; let print_mono k v fmt = @@ -90,97 +102,129 @@ module Th = struct ignore (Cl.M.fold print_mono v.poly fmt); Format.fprintf fmt "%a@]" Q.pp_print v.cst - let leaves t = Cl.M.map (fun _ -> ()) t.poly + (* let leaves t = Cl.M.map (fun _ -> ()) t.poly *) - let embed d cl = - Debug.dprintf debug "[Arith] @[embed cl=%a@]@." - (UnionFind.print_cl (E.Delayed.hack_env d)) cl; - match X.extract (E.Delayed.repr d cl) with + let embed s cl = + match s with | None -> monome Q.one cl | Some r -> r - let add d (v,cl) = - Cl.S.iter (fun leave -> - E.Delayed.depend_repr d cl leave) (leaves v) + (* let add d (v,cl) = *) + (* Cl.S.iter (fun leave -> *) + (* E.Delayed.depend_repr d cl leave) (leaves v) *) - let normalize d x = (** cst = 0 and one empty monome *) + let norm x = (** cst = 0 and one empty monome *) if Q.equal Q.zero x.cst && Cl.M.is_num_elt 1 x.poly then let cl,k = Cl.M.choose x.poly in - if Q.equal Q.one k then cl - else E.Delayed.normalize d (X.make x) - else E.Delayed.normalize d (X.make x) - - let subst d s t = - let t' = {cst = t.cst; poly = Cl.M.set_diff t.poly s} in - let t' = Cl.M.fold2_inter - (fun _ c p poly -> x_p_cy poly c (embed d p)) - t.poly s t' in - Debug.dprintf debug "[Arith] @[subst t=%a@ t'=%a@]@." - (X.print (E.Delayed.hack_env d)) t - (X.print (E.Delayed.hack_env d)) t'; - normalize d t' - - - let solve d (p1,cl1) cl2 = - let p2 = embed d cl2 in + if Q.equal Q.one k then NormalizeTo cl + else NormalizeToSem x + else NormalizeToSem x + + (* let subst d s t = *) + (* let t' = {cst = t.cst; poly = Cl.M.set_diff t.poly s} in *) + (* let t' = Cl.M.fold2_inter *) + (* (fun _ c p poly -> x_p_cy poly c (embed d p)) *) + (* t.poly s t' in *) + (* Debug.dprintf debug "[Arith] @[subst t=%a@ t'=%a@]@." *) + (* (print Cl.print) t *) + (* (print Cl.print) t'; *) + (* normalize d t' *) + + let subst_cl p x y = + let poly,q = Cl.M.find_remove x p.poly in + let poly = Cl.M.change (function + | None -> q + | Some q' -> none_zero (Q.add (Opt.get q) q') + ) y poly in + {p with poly} + + let subst_sem p x s = + let poly,q = Cl.M.find_remove x p.poly in + match q with + | None -> p + | Some q -> x_p_cy {p with poly} q s + + + let solve d (p1,cl1) (p2,cl2) = + let p2 = embed p2 cl2 in let p = sub p1 p2 in if Cl.M.is_empty p.poly then if Q.equal Q.zero p.cst - then raise E.SolveSameRepr - else raise E.Contradiction + then raise SolveSameRepr + else raise Contradiction else let x,q = Cl.M.choose p.poly in Debug.dprintf debug "[Arith] @[solve p=%a@]@." - (X.print (E.Delayed.hack_env d)) p; - let p = {p with poly = Cl.M.remove x p.poly} in + (print Cl.print) p; + let p' = {p with poly = Cl.M.remove x p.poly} in assert ( not (Q.equal Q.zero q) ); - let sr = normalize d (mult_cst (Q.inv (Q.neg q)) p) in - let s = Cl.M.singleton x sr in - Debug.dprintf debug "[Arith] @[solve q=%a x=%a@, sr=%a@]@." - Q.pp_print q - (UnionFind.print_cl (E.Delayed.hack_env d)) x - (UnionFind.print_cl (E.Delayed.hack_env d)) sr; - let sp1 = subst d s p1 in - assert ( let sp2 = subst d s p2 in - Debug.dprintf debug "[Arith] @[solve sp1=%a@, sp2=%a@]@." - (UnionFind.print_cl (E.Delayed.hack_env d)) sp1 - (UnionFind.print_cl (E.Delayed.hack_env d)) sp2; - Cl.equal sp1 sp2 ); - s, sp1 - - let subst d s t = - assert (not (Cl.M.is_empty (Cl.M.set_inter t.poly s))); - subst d s t - end + let sr = norm (mult_cst (Q.inv (Q.neg q)) p') in + let sp1 = begin match sr with + | NormalizeTo y -> + Debug.dprintf debug "[Arith] @[merge q=%a x=%a@, y=%a@]@." + Q.pp_print q Cl.print x Cl.print y; + Delayed.merge d x y; + subst_cl p x y + | NormalizeToSem s -> + Debug.dprintf debug "[Arith] @[set_sem q=%a x=%a@, s=%a@]@." + Q.pp_print q Cl.print x (print Cl.print) s; + Delayed.set_sem d sem x s; + subst_sem p x s + end in + (* assert ( let sp2 = subst d s p2 in *) + (* Debug.dprintf debug + "[Arith] @[solve sp1=%a@, sp2=%a@]@." *) + (* Cl.print sp1 Cl.print sp2; *) + (* Cl.equal sp1 sp2 ); *) + match norm sp1 with + | NormalizeTo y -> MergeTo y + | NormalizeToSem s -> MergeToSem s + + (* let subst d s t = *) + (* assert (not (Cl.M.is_empty (Cl.M.set_inter t.poly s))); *) + (* subst d s t *) + + let normalize d t = + let t' = Cl.M.fold + (fun x _ p -> + let x' = Delayed.normalize d x in + match Delayed.get_sem d sem x' with + | None when Cl.equal x x' -> p + | None -> subst_cl p x x' + | Some s -> subst_sem p x s) + t.poly t in + Debug.dprintf debug "[Arith] @[normalize %a@ into %a@]@." + (print Cl.print) t + (print Cl.print) t'; + norm t' + end -module M = E.MTerm.Make(Th) +module E = RegisterSem(Th) let embed t cl = - match M.extract (E.repr t cl) with + match get_sem t sem cl with | None -> monome Q.one cl | Some r -> r -let basecl t x = (** cst = 0 and one empty monome *) - if Q.equal Q.zero x.cst && Cl.M.is_num_elt 1 x.poly then - let cl,k = Cl.M.choose x.poly in - if Q.equal Q.one k then cl, t - else E.MTerm.UnionFind.basecl t (M.make x) - else E.MTerm.UnionFind.basecl t (M.make x) +let index t x = (** cst = 0 and one empty monome *) + match Th.norm x with + | NormalizeTo cl -> cl + | NormalizeToSem s -> index t sem s -type env = UnionFind.t +type env = Solver.t -let cst t c = basecl t (cst c) +let cst t c = index t (cst c) let add t cl1 cl2 = - basecl t (add (embed t cl1) (embed t cl2)) + index t (add (embed t cl1) (embed t cl2)) let sub t cl1 cl2 = - basecl t (sub (embed t cl1) (embed t cl2)) + index t (sub (embed t cl1) (embed t cl2)) let uninterp_mul env = Uninterp.fun2 env " *" @@ -189,10 +233,60 @@ let mult t cl1 cl2 = let m2 = embed t cl2 in if Cl.M.is_empty m1.poly || Cl.M.is_empty m2.poly then let cst,m = if Cl.M.is_empty m1.poly then m1.cst, m2 else m2.cst, m1 in - basecl t (mult_cst cst m) + index t (mult_cst cst m) else - let f,t = uninterp_mul t in + let f = uninterp_mul t in f t cl1 cl2 let mult_cst t cst cl = - basecl t (mult_cst cst (embed t cl)) + index t (mult_cst cst (embed t cl)) + + + + + exception SubstNoLeaves + + let rec add_daemon d arg s = + Debug.dprintf debug "[Arith] @[if_cl_change %a -> subst %a@]@." + Cl.print arg (Th.print Cl.print) s; + Delayed.if_cl_change d arg (subst_cl s); + Delayed.if_sem_change d arg sem (subst_s s); + + and subst_cl t3 d ~old:t1 t2 = + Debug.dprintf debug "[Arith] @[subst_cl %a[%a -> %a]@]@." + (Th.print Cl.print) t3 Cl.print t1 Cl.print t2; + match (Delayed.is_sem_present d sem t3) with + | None -> () + | Some cl3 -> + (* let s = Th.subst_cl t3 t1 t2 in *) + (* Cl.M.iter (fun k _ -> add_daemon d k s) s.poly; *) + (* Delayed.set_sem d sem cl3 s *) + match Th.normalize d t3 with + | NormalizeTo y -> Delayed.merge d cl3 y + | NormalizeToSem s -> + Cl.M.iter (fun k _ -> add_daemon d k t3) s.poly; + Cl.M.iter (fun k _ -> add_daemon d k s) s.poly; + Delayed.set_sem d sem cl3 s + + and subst_s t3 d t1 s = + Debug.dprintf debug "[Arith] @[subst_s %a[%a -> %a]@]@." + (Th.print Cl.print) t3 Cl.print t1 (Th.print Cl.print) s; + match (Delayed.is_sem_present d sem t3) with + | None -> () + | Some cl3 -> + (* let s = Th.subst_sem t3 t1 s in *) + (* Cl.M.iter (fun k _ -> add_daemon d k s) s.poly; *) + (* Delayed.set_sem d sem cl3 s *) + match Th.normalize d t3 with + | NormalizeTo y -> Delayed.merge d cl3 y + | NormalizeToSem s -> + (** Daemon are immediately added but set_sem is delayed ... *) + Cl.M.iter (fun k _ -> add_daemon d k t3) s.poly; (* big hack *) + Cl.M.iter (fun k _ -> add_daemon d k s) s.poly; + Delayed.set_sem d sem cl3 s + + + let propagate env = + Solver.if_propagate_sem env sem + ( fun d cl s -> + Cl.M.iter (fun k _ -> add_daemon d k s) s.poly) diff --git a/src/arith.mli b/src/arith.mli index 64d906d96..c582f3395 100644 --- a/src/arith.mli +++ b/src/arith.mli @@ -19,19 +19,19 @@ (* for more details (enclosed in the file licenses/LGPLv2.1). *) (* *) (**************************************************************************) +open Solver -open Term +type env = Solver.t -open Egraph_simple -open MTerm +type t -type env = UnionFind.t +val cst : env -> Q.t -> Cl.t +val add : env -> Cl.t -> Cl.t -> Cl.t +val sub : env -> Cl.t -> Cl.t -> Cl.t -val cst : env -> Q.t -> cl * env -val add : env -> cl -> cl -> cl * env -val sub : env -> cl -> cl -> cl * env +val mult_cst : env -> Q.t -> Cl.t -> Cl.t -val mult_cst : env -> Q.t -> cl -> cl * env +val mult : env -> Cl.t -> Cl.t -> Cl.t -val mult : env -> cl -> cl -> cl * env +val propagate : env -> unit diff --git a/src/inputlang/altergo/popop_of_altergo.ml b/src/inputlang/altergo/popop_of_altergo.ml index f3716921c..34881e1c0 100644 --- a/src/inputlang/altergo/popop_of_altergo.ml +++ b/src/inputlang/altergo/popop_of_altergo.ml @@ -29,17 +29,16 @@ let rec add_lexpr_term env t = match t.pp_desc with | PPvar s -> Uninterp.cst env s | PPapp (s,l) -> - let f,env = Uninterp.cst env s in - let env,l = Lists.map_fold_left - (fun env e -> let e,env = add_lexpr_term env e in env,e) env l in + let f = Uninterp.cst env s in + let l = List.map (fun e -> add_lexpr_term env e) l in Uninterp.appl env f l | PPconst (ConstInt s) -> Arith.cst env (Q.of_string s) | PPconst (ConstReal q) -> Arith.cst env q | PPinfix (t1,op,t2) -> - let cl1,env = add_lexpr_term env t1 in - let cl2,env = add_lexpr_term env t2 in + let cl1 = add_lexpr_term env t1 in + let cl2 = add_lexpr_term env t2 in let op = match op with | PPadd -> Arith.add | PPsub -> Arith.sub @@ -47,7 +46,7 @@ let rec add_lexpr_term env t = | _ -> raise (Not_supported (Loc.extract t.pp_loc)) in op env cl1 cl2 | PPprefix (PPneg,e) -> - let cl1, env = add_lexpr_term env e in + let cl1 = add_lexpr_term env e in Arith.mult_cst env (Q.of_int (-1)) cl1 | _ -> raise (Not_supported (Loc.extract t.pp_loc)) @@ -56,50 +55,45 @@ let uninterp_true env = Uninterp.cst env " true" let rec add_lexpr_axiom _true env t = match t.pp_desc with | PPapp (s,l) -> - let f,env = Uninterp.cst env s in - let env,l = Lists.map_fold_left - (fun env e -> let e,env = add_lexpr_term env e in env,e) env l in - let cl,env = Uninterp.appl env f l in - let cl,env = Egraph_simple.add env cl in - let env = Egraph_simple.equal env cl _true in - env + let f = Uninterp.cst env s in + let l = List.map (fun e -> add_lexpr_term env e) l in + let cl = Uninterp.appl env f l in + let cl = Solver.propagate env cl in + Solver.merge env cl _true | PPinfix (t1,PPand,t2) -> - let env = add_lexpr_axiom _true env t1 in - let env = add_lexpr_axiom _true env t2 in - env + add_lexpr_axiom _true env t1; + add_lexpr_axiom _true env t2 | PPinfix (t1,PPeq,t2) -> - let cl1,env = add_lexpr_term env t1 in - let cl1,env = Egraph_simple.add env cl1 in - let cl2,env = add_lexpr_term env t2 in - let cl2,env = Egraph_simple.add env cl2 in - let env = Egraph_simple.equal env cl1 cl2 in - env + let cl1 = add_lexpr_term env t1 in + let cl1 = Solver.propagate env cl1 in + let cl2 = add_lexpr_term env t2 in + let cl2 = Solver.propagate env cl2 in + Solver.merge env cl1 cl2 | _ -> raise (Not_supported (Loc.extract t.pp_loc)) let rec add_lexpr_goal _true env t = match t.pp_desc with | PPapp (s,l) -> - let f,env = Uninterp.cst env s in - let env,l = Lists.map_fold_left - (fun env e -> let e,env = add_lexpr_term env e in env,e) env l in - let cl,env = Uninterp.appl env f l in - let cl,env = Egraph_simple.add env cl in - Egraph_simple.is_equal env cl _true + let f = Uninterp.cst env s in + let l = List.map (fun e -> add_lexpr_term env e) l in + let cl = Uninterp.appl env f l in + let cl = Solver.propagate env cl in + Solver.is_equal env cl _true | PPinfix (t1,PPand,t2) -> let b1 = add_lexpr_goal _true env t1 in let b2 = add_lexpr_goal _true env t2 in b1 && b2 | PPinfix (t1,PPimplies,t2) -> - let env = add_lexpr_axiom _true env t1 in + add_lexpr_axiom _true env t1; let b2 = add_lexpr_goal _true env t2 in b2 | PPinfix (t1,PPeq,t2) -> - let cl1,env = add_lexpr_term env t1 in - let cl1,env = Egraph_simple.add env cl1 in - let cl2,env = add_lexpr_term env t2 in - let cl2,env = Egraph_simple.add env cl2 in - Egraph_simple.is_equal env cl1 cl2 + let cl1 = add_lexpr_term env t1 in + let cl1 = Solver.propagate env cl1 in + let cl2 = add_lexpr_term env t2 in + let cl2 = Solver.propagate env cl2 in + Solver.is_equal env cl1 cl2 | PPforall (_,_,_,e) | PPforall_named (_,_,_,e) -> add_lexpr_goal _true env e | _ -> raise (Not_supported (Loc.extract t.pp_loc)) @@ -107,12 +101,15 @@ let rec add_lexpr_goal _true env t = let rec add_decl _true env = function | [] -> raise (Not_supported Loc.dummy_position) - | Axiom(_,_,_,e)::l -> add_decl _true (add_lexpr_axiom _true env e) l + | Axiom(_,_,_,e)::l -> add_lexpr_axiom _true env e; add_decl _true env l | Goal(_,_,e)::_ -> add_lexpr_goal _true env e | _::l -> add_decl _true env l let check_goal l = - let _true,env = uninterp_true Egraph_simple.MTerm.UnionFind.empty in + let env = Solver.new_t () in + Uninterp.propagate env; + Arith.propagate env; + let _true = uninterp_true env in add_decl _true env l let loc lb = Loc.extract (Lexing.lexeme_start_p lb, Lexing.lexeme_end_p lb) diff --git a/src/solver.ml b/src/solver.ml index 51c4cc95f..b9d2f8cd1 100644 --- a/src/solver.ml +++ b/src/solver.ml @@ -29,6 +29,8 @@ let debug = Debug.register_info_flag exception BrokenInvariant of string exception Impossible +exception Contradiction +exception SolveSameRepr module Cl : sig include Datatype val create_cl : unit -> t end = struct include DInt @@ -51,6 +53,16 @@ module type Dom' = sig val key: t dom end +type 'a solveResult = +| NoMerge (** There is no representative for this solve (but not unsat) *) +| MergeTo of Cl.t +| MergeToSem of 'a + +type 'a normalizeResult = +| NormalizeTo of Cl.t +| NormalizeToSem of 'a + + module type Sem' = sig type delayed include OrderedHashedType @@ -60,13 +72,13 @@ module type Sem' = sig val print: (Format.formatter -> Cl.t -> unit) -> Format.formatter -> t -> unit - val normalize: delayed -> t -> Cl.t - val solve: delayed -> t * Cl.t -> t option * Cl.t -> Cl.t option + val normalize: delayed -> t -> t normalizeResult + val solve: delayed -> t * Cl.t -> t option * Cl.t -> t solveResult val key: t sem end type ('delayed,'a) dsem_event = - { dcallback: ('delayed -> Cl.t -> old:'a -> 'a -> unit) } + { dcallback: ('delayed -> Cl.t -> 'a -> unit) } type ('delayed,'a) propa_event = { pcallback: ('delayed -> Cl.t -> 'a -> unit) } @@ -191,6 +203,11 @@ let get_sem : 'a sem -> (module Sem with type t = 'a) = fun k -> (Obj.magic (sem : (module Sem)) : (module Sem with type t = 'a)) +let print_s (type a) (k : a sem) fmt s = + let sem = get_sem k in + let module S = (val sem : Sem with type t = a) in + S.print Cl.print fmt s + module type DomTable = DomTable' with type delayed = delayed_t module type SemTable = SemTable' with type delayed = delayed_t @@ -281,6 +298,59 @@ let get_table_sem : t -> 'a sem -> (module SemTable with type dt = 'a) Cl.M.find_opt cl SemTable.table +(** {2 For debugging and display} *) + +let output_graph filename t = + let open Graph in + let module G = struct + include Imperative.Digraph.Concrete(Cl) + let graph_attributes _ = [] + let default_vertex_attributes _ = [`Shape `Record] + let vertex_name cl = string_of_int (Cl.hash cl) + + let print fmt cl = + let iter fmt sem = + let module Sem = + (val sem : SemTable' with type delayed = delayed_t) in + try + let s = Cl.M.find cl Sem.table in + Sem.S.print Cl.print fmt s; + let cl' = Sem.S.M.find s Sem.invtable in + if Cl.equal cl cl' then Format.pp_print_string fmt "*"; + with Not_found -> () + in + Format.fprintf fmt "{%a | %a}" + Cl.print cl + (Pp.print_iter1 Simple_vector.iter_initialized + (Pp.constant_string " | ") iter) t.sem + + let vertex_attributes cl = + let label = Pp.string_of_wnl print cl in + [`Label label] + let default_edge_attributes _ = [] + let edge_attributes _ = [] + let get_subgraph _ = None + end in + let g = G.create () in + Cl.M.iter (fun cl1 cl2 -> G.add_edge g cl1 cl2) t.repr; + let cout = open_out filename in + let module Dot = Graphviz.Dot(G) in + Dot.output_graph cout g + +let show_graph = Debug.register_flag + ~desc:"Show each step in a gui" + "dotgui" + +let draw_graph = + let c = ref 0 in + fun t -> + if Debug.test_flag show_graph then + let filename = Format.sprintf "debug_graph%i.dot" !c in + incr c; + Debug.dprintf show_graph "[DotGui] output dot file: %s@." filename; + output_graph filename t + + (** {2 Delayed} *) module Delayed = struct @@ -297,11 +367,15 @@ module Delayed = struct let is_normalized t cl = Cl.M.mem cl t.env.repr let add_pending_merge (t : t) cl cl' = + Debug.dprintf debug "[Solver] @[add_pending_merge for %a and %a@]@." + Cl.print cl Cl.print cl'; assert (is_normalized t cl); assert (is_normalized t cl'); Queue.add (Merge (cl,cl')) t.todo - let add_pending_set_sem (type a) (t : t) (sem : 'a sem) cl (v:'a) = + let add_pending_set_sem (type a) (t : t) (sem : a sem) cl (v:a) = + Debug.dprintf debug "[Solver] @[add_pending_set_sem for %a and %a@]@." + Cl.print cl (print_s sem) v; assert (is_normalized t cl); Queue.add (SetSem (sem,cl,v)) t.todo @@ -322,31 +396,46 @@ module Delayed = struct (val sem : SemTable' with type delayed = delayed_t) in try let s = Cl.M.find cl Sem.table in - let cl' = Sem.S.normalize t s in - if Cl.equal cl' cl - then (* TODO: right place? *) + match Sem.S.normalize t s with + | NormalizeTo cl' when Cl.equal cl' cl -> (* TODO: right place? *) + List.iter (fun {pcallback = f} -> f t cl s) Sem.propagate + | NormalizeTo cl' -> add_pending_merge t cl cl'; + List.iter (fun {pcallback = f} -> f t cl s) Sem.propagate + | NormalizeToSem s' when Sem.S.equal s' s -> List.iter (fun {pcallback = f} -> f t cl s) Sem.propagate - else add_pending_merge t cl cl' + | NormalizeToSem s' -> add_pending_set_sem t Sem.S.key cl s'; + List.iter (fun {pcallback = f} -> f t cl s') Sem.propagate with Not_found -> () in Simple_vector.iter_initialized iter t.env.sem; - Cl.M.find_exn Impossible cl t.env.repr + let cl' = Cl.M.find_exn Impossible cl t.env.repr in + Debug.dprintf debug "[Solver] normalize %a to %a@." + Cl.print cl Cl.print cl'; + cl' + + let run_sem_event t events cl v = + match events with + | None -> () + | Some events -> + List.iter (fun {dcallback = f} -> f t cl v) events + let set_sem_pending (type a) t (sem : a sem) cl v = let module SemTable = (val (get_table_sem t.env sem) : SemTable with type dt = a) in let cl' = SemTable.S.M.find_opt v SemTable.invtable in + let new_event, events = Cl.M.find_remove cl SemTable.event in let module SemTable' : SemTable' with type delayed = delayed_t = struct (** remove the old semantical value replace it by the new one *) include SemTable - let table = - let n = Cl.M.remove cl SemTable.table in - if cl' = None then Cl.M.add cl v SemTable.table else n + let table = Cl.M.add cl v SemTable.table let invtable = if cl' = None then SemTable.S.M.add v cl invtable else invtable + let event = new_event end in Simple_vector.set t.env.sem sem (module SemTable'); + if cl' = None then run_sem_event t events cl v; match cl' with | None -> () | Some cl' -> @@ -366,35 +455,41 @@ module Delayed = struct let choose_repr cl1 cl2 = (Shuffle.shufflep (cl1,cl2)) - let merge_sem t cl1 cl2 repr_cl = + let merge_sem t cl1 cl2 other_cl repr_cl = assert (Cl.equal repr_cl cl1 || Cl.equal repr_cl cl2); let iter sem = let module Sem = (val sem : SemTable' with type delayed = delayed_t) in let s1 = Cl.M.find_opt cl1 Sem.table in let s2 = Cl.M.find_opt cl2 Sem.table in + Debug.dprintf debug "[Solver] @[merge sem (%a,%a)@ and (%a,%a)@]@." + Cl.print cl1 (Pp.print_option (Sem.S.print Cl.print)) s1 + Cl.print cl2 (Pp.print_option (Sem.S.print Cl.print)) s2; begin match s1,cl1,s2,cl2 with | Some s1, _ , Some s2, _ when Sem.S.equal s1 s2 -> let module SemTable' : SemTable' with type delayed = delayed_t = struct - (** remove the old semantical value - replace it by the new one *) + (** put the semantical value for the new representative class *) include Sem - let table = Cl.M.remove cl1 table + let table = Cl.M.remove cl1 table let table = Cl.M.remove cl2 table let table = Cl.M.add repr_cl s1 table + let cl_inv = Sem.S.M.find_exn Impossible s1 invtable + let invtable = if Cl.equal cl_inv other_cl then + Sem.S.M.add s1 repr_cl invtable else invtable end in Simple_vector.set t.env.sem Sem.S.key (module SemTable') | None, _, None, _ -> () | Some s, cl, None, cl' | None, cl', Some s, cl -> begin let clr = Sem.S.solve t (s,cl) (None,cl') in match clr with - | None -> (** The semantic value stays where it is *) () - | Some clr -> + | NoMerge -> (** The semantic value stays where it is *) () + | MergeTo clr -> let r = Cl.M.find_opt clr Sem.table in (** We remove cl -> s but we keep s -> cl (if s<>r otherwise its s -> repr_cl) *) + let new_event, events = Cl.M.find_remove cl Sem.event in let module SemTable' : SemTable' with type delayed = delayed_t = struct (** remove the old semantical value @@ -402,16 +497,30 @@ module Delayed = struct include Sem let table = Cl.M.remove cl table let table = Cl.M.add_opt repr_cl r table + let event = new_event end in Simple_vector.set t.env.sem Sem.S.key (module SemTable'); + Opt.iter (run_sem_event t events repr_cl) r; add_pending_merge t repr_cl clr + | MergeToSem r -> + let module SemTable' : + SemTable' with type delayed = delayed_t = struct + (** remove the old semantical value + replace it by the new one *) + include Sem + let table = Cl.M.remove cl table + let table = Cl.M.add repr_cl r table + end in + Simple_vector.set t.env.sem Sem.S.key (module SemTable'); + add_pending_set_sem t Sem.S.key repr_cl r end | Some s,cl, s', cl' -> let clr = Sem.S.solve t (s,cl) (s',cl') in match clr with - | None -> (** The semantic values stay where they are *) () - | Some clr -> + | NoMerge -> (** The semantic values stay where they are *) () + | MergeTo clr -> let r = Cl.M.find_opt clr Sem.table in + let new_event, events = Cl.M.find_remove cl Sem.event in let module SemTable' : SemTable' with type delayed = delayed_t = struct (** remove the old semantical value @@ -422,11 +531,24 @@ module Delayed = struct let table = Cl.M.add_opt repr_cl r table end in Simple_vector.set t.env.sem Sem.S.key (module SemTable'); + Opt.iter (run_sem_event t events repr_cl) r; add_pending_merge t repr_cl clr + | MergeToSem r -> + let module SemTable' : + SemTable' with type delayed = delayed_t = struct + (** remove the old semantical value + replace it by the new one *) + include Sem + let table = Cl.M.remove cl table + let table = Cl.M.remove cl' table + let table = Cl.M.add repr_cl r table + end in + Simple_vector.set t.env.sem Sem.S.key (module SemTable'); + add_pending_set_sem t Sem.S.key repr_cl r end in Simple_vector.iter_initialized iter t.env.sem - let merge_dom t cl1 cl2 repr_cl = () (* TODO *) + let merge_dom t cl1 cl2 other_cl repr_cl = () (* TODO *) (** merge two pending actions *) let merge_pending t cl1 cl2 = @@ -436,8 +558,8 @@ module Delayed = struct assert (is_repr t cl1); assert (is_repr t cl2); let other_cl, repr_cl = choose_repr cl1 cl2 in - merge_sem t cl1 cl2 repr_cl; - merge_dom t cl1 cl2 repr_cl; + merge_sem t cl1 cl2 other_cl repr_cl; + merge_dom t cl1 cl2 other_cl repr_cl; t.env.repr <- Cl.M.add other_cl repr_cl t.env.repr; (** wakeup the daemons *) let event, other_event = Cl.M.find_remove other_cl t.env.event in @@ -458,6 +580,7 @@ module Delayed = struct let set_dom t = assert false let rec do_pending t = + draw_graph t.env; if Queue.is_empty t.todo then Debug.dprintf debug "[Solver] @[do_pending Nothing@]@." else begin @@ -467,8 +590,8 @@ module Delayed = struct Cl.print cl1 Cl.print cl2; merge_pending t cl1 cl2 | SetSem (sem,cl,v) -> - Debug.dprintf debug "[Solver] @[do_pending SetSem %a@]@." - Cl.print cl; + Debug.dprintf debug "[Solver] @[do_pending SetSem %a %a@]@." + Cl.print cl (print_s sem) v; set_sem_pending t sem cl v end; do_pending t @@ -479,18 +602,34 @@ module Delayed = struct t.env.event <- Cl.M.add cl events t.env.event let if_dom_change t cl dom f = assert false - let if_sem_change t cl sem f = assert false + let if_sem_change (type a) t cl (sem : a sem) f = + let module SemTable = + (val (get_table_sem t.env sem) : SemTable with type dt = a) in + let module SemTable' : SemTable' with type delayed = delayed_t = struct + (** remove the old semantical value + replace it by the new one *) + include SemTable + let event = Cl.M.change (function + | None -> Some [{dcallback = f}] + | Some l -> Some ({dcallback = f}::l)) cl event + end in + Simple_vector.set t.env.sem sem (module SemTable') let if_propagate_sem t sem f = if_propagate_sem t.env sem f end + let propagate t cl = + draw_graph t; + Debug.dprintf debug "[Solver] @[propagate %a@]@." + Cl.print cl; let delayed = delayed_of_t t in let cl = Delayed.normalize delayed cl in Delayed.do_pending delayed; Cl.M.find_exn Impossible cl t.repr let merge t cl1 cl2 = + draw_graph t; let cl1,cl2 = Shuffle.shufflep (cl1,cl2) in Debug.dprintf debug "[Solver] @[merge %a %a@]@." Cl.print cl1 Cl.print cl2; @@ -502,6 +641,7 @@ let merge t cl1 cl2 = let is_equal t cl1 cl2 = + draw_graph t; let cl1,cl2 = Shuffle.shufflep (cl1,cl2) in Debug.dprintf debug "[Solver] @[is_equal %a %a@]@." Cl.print cl1 Cl.print cl2; @@ -512,41 +652,3 @@ let is_equal t cl1 cl2 = -(** {2 For debugging and display} *) - -let output_graph filename t = - let open Graph in - let module G = struct - include Imperative.Digraph.Concrete(Cl) - let graph_attributes _ = [] - let default_vertex_attributes _ = [`Shape `Record] - let vertex_name cl = string_of_int (Cl.hash cl) - - let print fmt cl = - let iter fmt sem = - let module Sem = - (val sem : SemTable' with type delayed = delayed_t) in - try - let s = Cl.M.find cl Sem.table in - Sem.S.print Cl.print fmt s; - let cl' = Sem.S.M.find s Sem.invtable in - if Cl.equal cl cl' then Format.pp_print_string fmt "*"; - with Not_found -> () - in - Format.fprintf fmt "{%a | {%a}}" - Cl.print cl - (Pp.print_iter1 Simple_vector.iter_initialized - (Pp.constant_string "| ") iter) t.sem - - let vertex_attributes cl = - let label = Pp.string_of_wnl print cl in - [`Label label] - let default_edge_attributes _ = [] - let edge_attributes _ = [] - let get_subgraph _ = None - end in - let g = G.create () in - Cl.M.iter (fun cl1 cl2 -> G.add_edge g cl1 cl2) t.repr; - let cout = open_out filename in - let module Dot = Graphviz.Dot(G) in - Dot.output_graph cout g diff --git a/src/solver.mli b/src/solver.mli index 8b3905dc9..5505a8101 100644 --- a/src/solver.mli +++ b/src/solver.mli @@ -24,6 +24,9 @@ open Stdlib (** {2 Types } *) +exception SolveSameRepr +exception Contradiction + (** Classes *) module Cl : Datatype @@ -33,7 +36,6 @@ type 'a sem type 'a dom (** key of a domain of type 'a *) - module Delayed : sig type t (** Immediate information *) @@ -55,10 +57,10 @@ module Delayed : sig (t -> old:Cl.t -> Cl.t -> unit) -> unit val if_dom_change : t -> Cl.t -> 'a dom -> - (t -> Cl.t -> old:'a -> 'a -> unit) -> unit + (t -> Cl.t -> 'a -> unit) -> unit val if_sem_change : t -> Cl.t -> 'a sem -> - (t -> Cl.t -> old:'a -> 'a -> unit) -> unit + (t -> Cl.t -> 'a -> unit) -> unit val if_propagate_sem : t -> 'a sem -> (t -> Cl.t -> 'a -> unit) -> unit @@ -82,6 +84,16 @@ end module RegisterDom (D:Dom) : sig end + +type 'a solveResult = +| NoMerge (** There is no representative for this solve (but not unsat) *) +| MergeTo of Cl.t +| MergeToSem of 'a + +type 'a normalizeResult = +| NormalizeTo of Cl.t +| NormalizeToSem of 'a + module type Sem = sig include OrderedHashedType module M : Map.S with type key = t @@ -90,8 +102,8 @@ module type Sem = sig val print: (Format.formatter -> Cl.t -> unit) -> Format.formatter -> t -> unit - val normalize: Delayed.t -> t -> Cl.t - val solve: Delayed.t -> t * Cl.t -> t option * Cl.t -> Cl.t option + val normalize: Delayed.t -> t -> t normalizeResult + val solve: Delayed.t -> t * Cl.t -> t option * Cl.t -> t solveResult val key: t sem end @@ -105,6 +117,7 @@ val index : t -> 'a sem -> 'a -> Cl.t val propagate: t -> Cl.t -> Cl.t val merge : t -> Cl.t -> Cl.t -> unit val is_equal : t -> Cl.t -> Cl.t -> bool +val get_sem : t -> 'a sem -> Cl.t -> 'a option val if_propagate_sem : t -> 'a sem -> (Delayed.t -> Cl.t -> 'a -> unit) -> unit diff --git a/src/uninterp.ml b/src/uninterp.ml index 23486db86..d810e9b48 100644 --- a/src/uninterp.ml +++ b/src/uninterp.ml @@ -30,7 +30,7 @@ type t = | Cst of Strings.Hashcons.t | App of Cl.t * Cl.t -let sem = create_sem_key () +let sem : t sem = create_sem_key () module T = struct type r = t @@ -81,15 +81,20 @@ module Th = struct let add d _ = () (* No more classes to merge than the two given class themselves *) - let solve _d _ _ = None + let solve _d _ _ = NoMerge let norm d s = Delayed.normalize d (Delayed.index d sem s) let normalize d s = match s with - | Cst _ -> norm d s - | App(f,g) -> norm d (App(Delayed.normalize d f, Delayed.normalize d g)) + | Cst _ -> + NormalizeToSem s + | App(f,g) -> + let s' = (App(Delayed.normalize d f, Delayed.normalize d g)) in + Debug.dprintf debug "[Uninterp] @[normalize %a to %a@]@." + (print Cl.print) s (print Cl.print) s'; + NormalizeToSem s' end module Registered = RegisterSem(Th) @@ -134,6 +139,9 @@ module Registered = RegisterSem(Th) Delayed.if_cl_change d arg (subst s) and subst t3 d ~old:t1 t2 = + Debug.dprintf debug "[Uninterp] @[subst %a[%a -> %a]@]@." + (Th.print Cl.print) t3 Cl.print t1 Cl.print t2; + Debug.dprintf debug "[Uninterp] @[subst %a[%a -> %a]@]@." (Th.print Cl.print) t3 Cl.print t1 Cl.print t2; match (Delayed.is_sem_present d sem t3), t3 with @@ -143,7 +151,7 @@ module Registered = RegisterSem(Th) assert ( if not ( Cl.equal t1 f || Cl.equal t1 g) then raise SubstNoLeaves else true); - let replace arg = if Cl.equal arg t1 then t2 else arg in + let replace arg = Delayed.normalize d arg in let f = replace f in let g = replace g in let s = App (f,g) in diff --git a/tests/tests_lib.ml b/tests/tests_lib.ml index be92fa9d9..48a62c2a2 100644 --- a/tests/tests_lib.ml +++ b/tests/tests_lib.ml @@ -11,13 +11,13 @@ let test_split s = s >::: begin let l = Popop_of_altergo.read_split s in List.map (fun (res,loc,d) -> - (Pp.string_of Loc.gen_report_position loc) >:: - fun () -> + TestCase (fun () -> + let s = Pp.string_of_wnl Loc.gen_report_position loc in let b = (Popop_of_altergo.check_goal d) in match res with - | "Valid" -> assert_bool "" b - | "Unknown" -> assert_bool "" (not b) - | _ -> invalid_arg "Unknown status" + | "Valid" -> assert_bool s b + | "Unknown" -> assert_bool s (not b) + | _ -> invalid_arg "Unknown status") ) l end diff --git a/tests/tests_uf.ml b/tests/tests_uf.ml index ad37332d3..0ce690149 100644 --- a/tests/tests_uf.ml +++ b/tests/tests_uf.ml @@ -185,10 +185,10 @@ let altergo = "altergo tests" >::: [altergo1; altergo2] let files = ["tests/tests_altergo_qualif.split"] -(* let altergo2 = TestList (List.map Tests_lib.test_split files) *) +let altergo2 = TestList (List.map Tests_lib.test_split files) -let tests = TestList [basic;congru1;congru2;altergo;(* altergo2 *)] +let tests = TestList [basic;congru1;congru2;altergo;altergo2] end -- GitLab