diff options
author | Alex Auvolat <alex.auvolat@ansys.com> | 2014-06-24 17:12:04 +0200 |
---|---|---|
committer | Alex Auvolat <alex.auvolat@ansys.com> | 2014-06-24 17:12:04 +0200 |
commit | 99cf7409d4eaacd7f8b86ff4de8b92c86a10dd5f (patch) | |
tree | 4fddba970a4a5db064d48859c9525baff4d4ae6f /abstract/abs_interp.ml | |
parent | 6dec5b2fae34f4be8e57745e3b1a7063a62e1fc4 (diff) | |
download | scade-analyzer-99cf7409d4eaacd7f8b86ff4de8b92c86a10dd5f.tar.gz scade-analyzer-99cf7409d4eaacd7f8b86ff4de8b92c86a10dd5f.zip |
Implementation of disjunction domain seems to work.
Diffstat (limited to 'abstract/abs_interp.ml')
-rw-r--r-- | abstract/abs_interp.ml | 468 |
1 files changed, 390 insertions, 78 deletions
diff --git a/abstract/abs_interp.ml b/abstract/abs_interp.ml index a610843..8d5f1c9 100644 --- a/abstract/abs_interp.ml +++ b/abstract/abs_interp.ml @@ -4,42 +4,244 @@ open Formula open Typing open Util -open Abs_domain open Num_domain +open Enum_domain -module I (E : ENVIRONMENT_DOMAIN) : sig + +module I (ED : ENUM_ENVIRONMENT_DOMAIN) (ND : NUMERICAL_ENVIRONMENT_DOMAIN) : sig type st - val do_prog : int -> rooted_prog -> unit + val do_prog : int -> (id -> bool) -> rooted_prog -> unit end = struct + (* ************************** + Disjunction domain + ************************** *) + + type case_v = ED.t * ND.t + type case = ED.item list + + type dd_v = { + d_vars : id list; + data : (case, case_v) Hashtbl.t; + } + + (* + print_dd : Formatter.t -> dd_v -> unit + *) + let print_aux fmt d_vars env = + let print_it q (enum, num) = + Format.fprintf fmt "@[<hov 4>"; + List.iter2 + (fun id v -> Format.fprintf fmt "%a ≡ %s,@ " + Formula_printer.print_id id v) + d_vars q; + Format.fprintf fmt "@ %a,@ %a@]@." + ED.print enum ND.print num; + in + Hashtbl.iter print_it env + let print_dd fmt dd = print_aux fmt dd.d_vars dd.data + + + (* + dd_bot : id list -> dd_v + *) + let dd_bot d_vars = { d_vars; data = Hashtbl.create 12 } + + (* + dd_add_case : dd_v -> case_v -> unit + *) + let dd_add_case env (enum, num) = + let rec aux enum vals = function + | [] -> + let case = List.rev vals in + begin try let e0, n0 = Hashtbl.find env.data case in + Hashtbl.replace env.data case (ED.join enum e0, ND.join num n0); + with Not_found -> + Hashtbl.add env.data case (enum, num); + end + | p::q -> + List.iter + (fun v -> match ED.apply_cons enum (E_EQ, p, EItem v) with + | [en] -> aux en (v::vals) q + | _ -> assert false) + (ED.project enum p) + in aux enum [] env.d_vars + + (* + dd_singleton : id list -> case_v -> dd_v + *) + let dd_singleton d_vars d = + let v = { d_vars; data = Hashtbl.create 1 } in + dd_add_case v d; + v + + (* + dd_extract_case : dd_v -> case -> dd_v + *) + let dd_extract_case v case = + try dd_singleton v.d_vars (Hashtbl.find v.data case) + with Not_found -> dd_bot v.d_vars + + + (* + dd_apply_ncons : dd_v -> num_cons list -> dd_v + *) + let dd_apply_ncl v ncl = + let vv = dd_bot v.d_vars in + Hashtbl.iter (fun case (enum, num) -> + let num = ND.apply_cl num ncl in + if not (ND.is_bot num) then + Hashtbl.add vv.data case (enum, num)) + v.data; + vv + + (* + dd_apply_econs : dd_v -> enum_cons -> dd_v + *) + let dd_apply_econs v cons = + let vv = dd_bot v.d_vars in + Hashtbl.iter (fun case (enum, num) -> + List.iter + (fun enum -> dd_add_case vv (enum, num)) + (ED.apply_cons enum cons)) + v.data; + vv + + (* + dd_join : dd_v -> dd_v -> dd_v + *) + let dd_join a b = + let vv = { d_vars = a.d_vars; data = Hashtbl.copy a.data } in + Hashtbl.iter (fun case (enum, num) -> + try let e2, n2 = Hashtbl.find vv.data case in + Hashtbl.replace vv.data case (ED.join e2 enum, ND.join n2 num) + with Not_found -> Hashtbl.replace vv.data case (enum, num)) + b.data; + vv + + (* + dd_meet : dd_v -> dd_v -> dd_v + *) + let dd_meet a b = + let vv = dd_bot a.d_vars in + Hashtbl.iter (fun case (enum, num) -> + try let e2, n2 = Hashtbl.find b.data case in + let en, nn = ED.meet enum e2, ND.meet n2 num in + if not (ND.is_bot nn) then Hashtbl.add vv.data case (en, nn) + with _ -> ()) + a.data; + vv + + (* + dd_apply_cl : dd_v -> conslist -> dd_v + *) + let rec apply_cl env (ec, nc, r) = + match r with + | CLTrue -> + let env_ec = List.fold_left dd_apply_econs env ec in + let env_nc = dd_apply_ncl env_ec nc in + (* Format.printf "[[ %a -> %a ]]@." + print_dd env print_dd env_nc; *) + env_nc + | CLFalse -> dd_bot env.d_vars + | CLAnd(a, b) -> + let env = apply_cl env (ec, nc, a) in + apply_cl env (ec, nc, b) + | CLOr((eca, nca, ra), (ecb, ncb, rb)) -> + dd_join (apply_cl env (ec@eca, nc@nca, ra)) + (apply_cl env (ec@ecb, nc@ncb, rb)) + + + (* *************************** + Abstract interpretation + *************************** *) + type st = { rp : rooted_prog; widen_delay : int; - env : E.t; + + enum_vars : (id * ED.item list) list; + num_vars : (id * bool) list; + + (* program expressions *) f : bool_expr; cl : conslist; f_g : bool_expr; cl_g : conslist; guarantees : (id * bool_expr) list; + + (* abstract interpretation *) + cycle : (id * id * typ) list; (* s'(x) = s(y) *) + forget : (id * typ) list; (* s'(x) not specified *) + d_vars : id list; (* disjunction variables *) + top : case_v; + env : (case, case_v) Hashtbl.t; + mutable delta : case list; + widen : (case, int) Hashtbl.t; } + (* + print_st : Formatter.t -> st -> unit + *) + let print_st fmt st = print_aux fmt st.d_vars st.env - (* init state *) - let init_state widen_delay rp = - let env = List.fold_left - (fun env (_, id, ty) -> E.addvar env id ty) - E.init - rp.all_vars - in + (* + add_case : st -> case_v -> unit + *) + let add_case st (enum, num) = + let rec aux enum vals = function + | [] -> + let case = List.rev vals in + begin try let e0, n0 = Hashtbl.find st.env case in + if (not (ED.subset enum e0)) || (not (ND.subset num n0)) then begin + begin try + let n = Hashtbl.find st.widen case in + let jw = if n < st.widen_delay then ND.join else ND.widen in + Hashtbl.replace st.env case (ED.join e0 enum, jw n0 num); + Hashtbl.replace st.widen case (n+1); + with Not_found -> + Hashtbl.replace st.env case (ED.join enum e0, ND.join num n0); + Hashtbl.add st.widen case 0; + end; + if not (List.mem case st.delta) then st.delta <- case::st.delta + end + with Not_found -> + Hashtbl.add st.env case (enum, num); + if not (List.mem case st.delta) then st.delta <- case::st.delta + end + | p::q -> + List.iter + (fun v -> match ED.apply_cons enum (E_EQ, p, EItem v) with + | [en] -> aux en (v::vals) q + | _ -> assert false) + (ED.project enum p) + in aux enum [] st.d_vars + + + + (* + init_state : int -> rooted_prog -> st + *) + let init_state widen_delay disj rp = Format.printf "Vars: @[<hov>%a@]@.@." (Ast_printer.print_list Ast_printer.print_typed_var ", ") rp.all_vars; + let enum_vars = List.fold_left + (fun v (_, id, t) -> match t with + | TEnum ch -> (id, ch)::v | _ -> v) + [] rp.all_vars in + let num_vars = List.fold_left + (fun v (_, id, t) -> match t with + | TInt -> (id, false)::v | TReal -> (id, true)::v | _ -> v) + [] rp.all_vars in + let init_f = Transform.init_f_of_prog rp in Format.printf "Init formula: %a@.@." Formula_printer.print_expr init_f; + let init_cl = conslist_of_f init_f in let guarantees = Transform.guarantees_of_prog rp in Format.printf "Guarantees:@."; @@ -51,96 +253,192 @@ end = struct let f = Formula.eliminate_not (Transform.f_of_prog rp false) in let f_g = Formula.eliminate_not (Transform.f_of_prog rp true) in Format.printf "Cycle formula:@.%a@.@." Formula_printer.print_expr f; - Format.printf "Cycle formula with guarantees:@.%a@.@." Formula_printer.print_expr f_g; - - let env = E.set_disjunct env "/exit" DNever in - let env = E.apply_f env init_f in + Format.printf "Cycle formula with guarantees:@.%a@.@." + Formula_printer.print_expr f_g; let cl = Formula.conslist_of_f f in let cl_g = Formula.conslist_of_f f_g in - { rp; widen_delay; - f; cl; f_g; cl_g; - guarantees; env } - - (* - pass_cycle : var_def list -> E.t -> E.t - - Does : - - x <- Nx, for all x - - ignore x for all x such that Nx does not exist - *) - let pass_cycle vars e = - let tr_assigns = List.fold_left - (fun q (_, id, _) -> + (* calculate cycle variables and forget variables *) + let cycle = List.fold_left + (fun q (_, id, ty) -> if id.[0] = 'N' then - (String.sub id 1 (String.length id - 1), id)::q - else - q) - [] vars + (String.sub id 1 (String.length id - 1), id, ty)::q + else q) + [] rp.all_vars in - let e = E.assign e tr_assigns in - - let forget_vars = List.map (fun (_, id, _) -> id) + let forget = List.map (fun (_, id, ty) -> (id, ty)) (List.filter - (fun (_, id, _) -> - not (List.exists (fun (_, id2, _) -> id2 = "N"^id) vars)) - vars) in - let e = List.fold_left E.forgetvar e forget_vars in - e + (fun (_, id, _) -> + not (List.exists (fun (_, id2, _) -> id2 = "N"^id) rp.all_vars)) + rp.all_vars) + in + + (* calculate disjunction variables *) + (* first approximation : use all enum variables appearing in init formula *) + let rec calculate_dvars (ec, _, r) = + let a = List.map (fun (_, id, _) -> id) ec in + let rec aux = function + | CLTrue | CLFalse -> [] + | CLAnd(u, v) -> + aux u @ aux v + | CLOr(u, v) -> + calculate_dvars u @ calculate_dvars v + in a @ aux r + in + let d_vars_0 = calculate_dvars init_cl in + let d_vars_1 = List.filter + (fun id -> disj id && not (List.mem id d_vars_0) && id.[0] <> 'N') + (List.map (fun (id, _) -> id) enum_vars) in + let d_vars = d_vars_0 @ d_vars_1 in + + (* setup abstract data *) + let top = ED.top enum_vars, ND.top num_vars in + let delta = [] in + let widen = Hashtbl.create 12 in + (* calculate initial state and environment *) + let init_s = dd_bot d_vars in + + let init_ec, _, _ = init_cl in + let init_ed = List.fold_left + (fun ed cons -> + List.flatten (List.map (fun x -> ED.apply_cons x cons) ed)) + [ED.top enum_vars] (init_ec) in + List.iter (fun ed -> dd_add_case init_s (ed, ND.top num_vars)) init_ed; + let env_dd = apply_cl init_s init_cl in + let env = env_dd.data in + + let st = + { rp; widen_delay; enum_vars; num_vars; + f; cl; f_g; cl_g; guarantees; + cycle; forget; d_vars; top; + env; delta; widen } in + + Hashtbl.iter (fun case _ -> st.delta <- case::st.delta) st.env; + + st - (* cycle : st -> cl -> env -> env *) - let cycle st cl i = - (pass_cycle st.rp.all_vars (E.apply_cl i cl)) (* - loop : st -> env -> env -> env + pass_cycle : st -> case_v -> case_v *) - let loop st acc0 = + let pass_cycle st (enum, num) = + let assign_e = + List.map (fun (a, b, _) -> a, b) + (List.filter + (fun (_, _, t) -> match t with TEnum _ -> true | _ -> false) + st.cycle) in + let assign_n = + List.map (fun (a, b, _) -> a, NIdent b) + (List.filter + (fun (_, _, t) -> match t with TInt | TReal -> true | _ -> false) + st.cycle) in - Format.printf "Loop @[<hv>%a@]@.@." - Formula_printer.print_conslist st.cl; + let enum = ED.assign enum assign_e in + let num = ND.assign num assign_n in - let fsharp cl i = - Format.printf "Acc @[<hv>%a@]@." E.print_all i; - let j = cycle st cl i in - E.join acc0 j - in + List.fold_left + (fun (enum, num) (var, t) -> match t with + | TEnum _ -> ED.forgetvar enum var, num + | TReal | TInt -> enum, ND.forgetvar num var) + (enum, num) st.forget - let rec iter n i = - let i' = - if n < st.widen_delay - then E.join i (fsharp st.cl i) - else E.widen i (fsharp st.cl_g i) - in - if E.eq i i' then i else iter (n+1) i' - in + let dd_pass_cycle st (v : dd_v) = + let vv = dd_bot v.d_vars in + Hashtbl.iter (fun _ x -> dd_add_case vv (pass_cycle st x)) v.data; + vv + + (* + cycle : st -> cl -> dd_v -> dd_v + *) + let cycle st cl env = + let env_f = apply_cl env cl in + (* Format.printf "{{ %a ->@.%a }}@." print_dd env print_dd env_f; *) + dd_pass_cycle st env_f - let x = iter 0 acc0 in - let y = fix E.eq (fsharp st.cl) x in (* decreasing iteration *) - let z = E.apply_cl y st.cl in (* no more guarantee *) - y, z + + (* + set_target_case : st -> dd_v -> case -> dd_v + *) + let set_target_case st env case = + List.fold_left2 + (fun env id v -> + if List.exists (fun (id2, _) -> id2 = "N"^id) st.enum_vars then + dd_apply_econs env (E_EQ, "N"^id, EItem v) + else env) + env st.d_vars case (* do_prog : rooted_prog -> unit *) - let do_prog widen_delay rp = - let st = init_state widen_delay rp in + let do_prog widen_delay disj rp = + let st = init_state widen_delay disj rp in + + Format.printf "Init: %a@." print_st st; + + let n_ch_iter = ref 0 in - let final_nc, final = loop st st.env in + (* chaotic iteration loop *) + while st.delta <> [] do + let case = List.hd st.delta in + + incr n_ch_iter; + Format.printf "Chaotic iteration %d: @[<hov>[%a]@]@." + !n_ch_iter + (Ast_printer.print_list Formula_printer.print_id ", ") case; + + let start = try Hashtbl.find st.env case with Not_found -> assert false in + let start_dd = dd_singleton st.d_vars start in + let f i = + let i = dd_singleton st.d_vars i in + let i' = set_target_case st i case in + let q = dd_join start_dd (cycle st st.cl i') in + try Hashtbl.find q.data case with Not_found -> assert false + in + + let rec iter n (i_enum, i_num) = + let fi_enum, fi_num = f (i_enum, i_num) in + let j_enum, j_num = + if n < st.widen_delay then + ED.join i_enum fi_enum, ND.join i_num fi_num + else + ED.join i_enum fi_enum, ND.widen i_num fi_num + in + if ED.eq j_enum i_enum && ND.eq j_num i_num + then (i_enum, i_num) + else iter (n+1) (j_enum, j_num) + in + let y = iter 0 start in + let z = fix (fun (a, b) (c, d) -> ED.eq a c && ND.eq b d) f y in + + let i = dd_singleton st.d_vars z in + let j = cycle st st.cl i in + let cases = ref [] in + Hashtbl.iter (fun case _ -> cases := case::(!cases)) j.data; + List.iter + (fun case -> + let i' = set_target_case st i case in + let j = cycle st st.cl i' in + Hashtbl.iter (fun _ q -> add_case st q) j.data) + !cases; + + st.delta <- List.filter ((<>) case) st.delta; + + (* Format.printf "Final: %a@." print_st st; *) + + done; + + let final = apply_cl { d_vars = st.d_vars; data = st.env } st.cl in + Format.printf "Accessible: %a@." print_dd final; - Format.printf "@[<hov>F %a@]@.@." - E.print_all final_nc; - Format.printf "@[<hov>F' %a@]@.@." - E.print_all final; let check_guarantee (id, f) = let cl = Formula.conslist_of_f f in Format.printf "@[<hv 4>%s:@ %a ⇒ ⊥ @ " id Formula_printer.print_conslist cl; - let z = E.apply_cl final cl in - if E.is_bot z then + let z = apply_cl final cl in + if Hashtbl.length z.data = 0 then Format.printf "OK@]@ " else Format.printf "FAIL@]@ " @@ -153,12 +451,26 @@ end = struct if List.exists (fun (p, _, _) -> p) st.rp.all_vars then begin Format.printf "Probes: @[<v 0>"; - List.iter (fun (p, id, _) -> - if p then Format.printf "%a ∊ %a@ " - Formula_printer.print_id id - E.print_itv (E.var_itv final id)) + List.iter (fun (p, id, ty) -> + if p then begin + Format.printf "%a ∊ @[<v>" Formula_printer.print_id id; + Hashtbl.iter (fun case (enum, num) -> + begin match ty with + | TInt | TReal -> + Format.printf "%a" ND.print_itv (ND.project num id) + | TEnum _ -> + Format.printf "@[<hov>{ %a }@]" + (Ast_printer.print_list Formula_printer.print_id ", ") + (ED.project enum id) + end; + Format.printf " when @[<hov>[%a]@]@ " + (Ast_printer.print_list Formula_printer.print_id ", ") case) + final.data; + Format.printf "@]@ " + end) st.rp.all_vars; Format.printf "@]@." end end + |