-
Notifications
You must be signed in to change notification settings - Fork 17
Map Elimination Implementation #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
nikhilpim
wants to merge
20
commits into
zkincaid:flint
Choose a base branch
from
nikhilpim:array-pr
base: flint
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
5bb985c
arrayify initial commit
5a4b374
supporting return nodes in boogiegraph->weightedgraph translation
a82e534
Array lifting procedure initial implementation
fd89a7a
dealing with bruijn indices
2c4154b
checkpoint
c9d5e89
fixed skolemization
92d6978
exposed bubble
666b487
cleanup for pr
748559e
removing arrayify for smaller pr
ecd1540
switched rep_map to be a hashtable
09fed50
fixed PR requests
27c6be5
missed some comments
04c91f6
implemented new tests
6f5b428
added new tests
551007d
bug fixes
0a37ecf
small cleanup
1d3d5f2
pr changes
40c8abd
refactored to use fold_rewrite and moved existential/fc to array exis…
89c9dd1
minor pr issues
cfbe80b
factored out forall computation
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| open Syntax | ||
| open Iteration | ||
|
|
||
| (* bubble_sym is an internal helper function for lowering formulas in the skolem array fragment | ||
| to quantified numerical formulas. | ||
| [bubble_sym srk form] returns a triple of the form (syms, fr, form') in which: | ||
| - form ==> \exists syms. \forall fr. form' | ||
| - form' contains no array terms | ||
| *) | ||
| let bubble_sym (srk : 'a context) (form : 'a formula) : (symbol list * (symbol) * 'a formula) = | ||
| (* rep_map : arr_var -> (index_term -> var) *) | ||
| let (rep_map : (symbol, ('a, Syntax.typ_arith, symbol) Expr.HT.t) Hashtbl.t) = Hashtbl.create 16 in | ||
| let leading_existentials = ref [] in | ||
| let forall_symbol = mk_symbol srk `TyInt ~name:"forall_index" in | ||
|
|
||
| let get_or_create_replacement array_symbol index_term = | ||
| let replacement_map = Hashtbl.find rep_map array_symbol in | ||
| match Expr.HT.mem replacement_map index_term with | ||
| | true -> mk_const srk (Expr.HT.find replacement_map index_term) | ||
| | false -> ( | ||
| let name = Format.asprintf "rep_%a_%a" | ||
| (pp_symbol srk) array_symbol | ||
| (ArithTerm.pp srk) index_term | ||
| in | ||
| let replacement = mk_symbol srk `TyInt ~name in | ||
| Expr.HT.add replacement_map index_term replacement; | ||
| leading_existentials := replacement :: !leading_existentials; (* Update the mutable list *) | ||
| mk_const srk replacement | ||
| ) | ||
| in | ||
|
|
||
| (* All variables are turned into symbols as we recurse across the formula. | ||
| env maps from variable indices to symbols. | ||
| qo is the index of the forall variable. | ||
| Variables with index > qo are existentially quantified outside the forall. | ||
| Note that replace functions only get run after we have found the forall. | ||
| *) | ||
| (* [replace_access arr i env qo] returns a substitute term for arr[i] *) | ||
| let rec replace_access (arr : 'a arr_term) (index : 'a arith_term) (env : (symbol Env.t)) (qo : int) : 'a arith_term = | ||
| match ArrTerm.destruct srk arr with | ||
| | `Var (i, _) -> | ||
| if (i <= qo) then (failwith "Array term is not quantified inside the forall") else ( | ||
| let array_symbol = Env.find env (i - qo) in | ||
| match ArithTerm.destruct srk index with | ||
| | `Var (indi, _) -> | ||
| ((if indi < qo then failwith "Array index variable is bound but should be free"); | ||
| let index_symbol = Env.find env (indi - qo) in | ||
| get_or_create_replacement array_symbol (mk_const srk index_symbol)) | ||
| | `App (_, []) | `Real _ -> | ||
| get_or_create_replacement array_symbol index | ||
| | _ -> failwith "Array index should be a variable, symbol, or constant" | ||
| ) | ||
| | `App (_, _) -> failwith "Array applications not supported" | ||
| | `Ite (f, l, r) -> mk_ite srk f (replace_access l index env qo) (replace_access r index env qo) | ||
| | `Store (a, store_index, value) -> | ||
| let ae = replace_access a index env qo in | ||
| mk_ite srk (mk_eq srk store_index index) value ae | ||
| in | ||
| let replace env f = | ||
| fold_rewrite srk | ||
| ~down:(fun e qo -> match destruct srk e with | `Var (i, t) -> if i >= qo && t != `TyArr then (mk_const srk (Env.find env (i - qo))) else e | _ -> e) | ||
| ~up:(fun e qo -> match destruct srk e with | `Select (arr, index) -> replace_access arr index env qo | _ -> e) | ||
| ~combine:(fun e qo -> match destruct srk e with | `Quantify (`Exists, _, _, _) -> qo + 1 | `Quantify (`Forall, _, _, _) -> failwith "Nested foralls" | _ -> qo) | ||
| 0 f | ||
| in | ||
|
|
||
| (* go contains the core "bubbling" logic of this algorithm: | ||
| we process leading existentials, skolemizing them and adding them to env. | ||
| Once we reach a forall, we stop processing existentials and switch over to replace functions | ||
| to eliminate array terms. | ||
| *) | ||
| (* [go form env] considers the formula \exists env . form | ||
| and returns form' where | ||
| - form' contains no array terms | ||
| - \exists env . form ==> \exists syms. \forall forall_sym. form' | ||
| *) | ||
| let rec go (f : 'a formula) (env : (symbol Env.t)): ('a formula) = | ||
| match Formula.destruct srk f with | ||
| | `Quantify (`Exists, name, typ, body) -> | ||
| let new_sym = mk_symbol srk ~name (typ :> typ) in | ||
| (if typ = `TyArr then ( | ||
| Hashtbl.add rep_map new_sym (Expr.HT.create 16); | ||
| let name = Format.asprintf "rep_%a_univ" | ||
| (pp_symbol srk) new_sym | ||
| in | ||
| Expr.HT.add (Hashtbl.find rep_map new_sym) (mk_const srk forall_symbol) (mk_symbol srk `TyInt ~name); | ||
| let body' = go body (Env.push new_sym env) in | ||
| let ai = get_or_create_replacement new_sym (mk_const srk forall_symbol) in | ||
| let fc = BatEnum.fold (fun acc (index, replacement) -> | ||
| (mk_if srk (mk_eq srk index (mk_const srk forall_symbol)) (mk_eq srk (mk_const srk replacement) ai)) :: acc | ||
| ) [] (Expr.HT.enum (Hashtbl.find rep_map new_sym)) in | ||
| let body'' = mk_exists_const srk (Expr.HT.find (Hashtbl.find rep_map new_sym) (mk_const srk forall_symbol)) | ||
| (mk_and srk ((body') :: fc)) | ||
| in | ||
| body'' | ||
| ) else ( | ||
| leading_existentials := new_sym :: !leading_existentials; | ||
| go body (Env.push new_sym env) | ||
| )) | ||
| | `Quantify (`Forall, _, typ, body) -> | ||
| assert (typ = `TyInt); | ||
| replace (Env.push forall_symbol env) body | ||
| | `And ls -> | ||
| let (parts) = List.fold_left (fun parts f -> | ||
| go f env :: parts | ||
| ) [] ls in | ||
| (mk_and srk parts) | ||
| | `Or ls -> | ||
| let branch_var = mk_symbol srk ~name:"branch" `TyInt in | ||
| (leading_existentials := branch_var :: !leading_existentials); | ||
| let (parts) = List.fold_left (fun parts f -> | ||
| go f env :: parts | ||
| ) [] ls in | ||
| let parts = List.mapi (fun i part -> mk_and srk [part; mk_eq srk (mk_const srk branch_var) (mk_int srk i)]) parts in | ||
| (mk_or srk parts) | ||
| | `Atom _ | `Proposition _ -> | ||
| (* At this point, we want to run replace but cannot because we haven't yet seen a forall. | ||
| We use the equivalence f <=> \forall d. f where f is d-free | ||
| to map back to the forall case. | ||
| *) | ||
| let f' = substitute srk (fun (i, typ) -> mk_var srk (i + 1) typ) f in | ||
| go (mk_forall srk `TyInt f') env | ||
| | `Tru | `Fls -> (f) | ||
| | `Ite (f, l, r) -> | ||
| let f' = mk_or srk [mk_and srk [f ; l]; mk_and srk [mk_not srk f; r]] in | ||
| let f' = rewrite srk ~down:(pos_rewriter srk) f' in | ||
| go f' env | ||
| | `Not _ -> failwith "Not is not supported in Skolem fragment" | ||
| in | ||
| let retf = go form Syntax.Env.empty in | ||
| (!leading_existentials, forall_symbol, retf) | ||
|
|
||
|
|
||
|
|
||
| (* array_exponentiate lifts exponentiation operator e to work over formulas with arrays. *) | ||
| let array_exponentiate (srk : 'a context) (e : 'a exp_op) : ('a TransitionFormula.t -> 'a Syntax.arith_term -> 'a Syntax.formula) = | ||
| fun (tf : 'a TransitionFormula.t) (k : 'a arith_term) -> | ||
| let f = TransitionFormula.formula tf in | ||
| let array_symbols = List.filter (fun (s, s') -> typ_symbol srk s = `TyArr && typ_symbol srk s' = `TyArr) (TransitionFormula.symbols tf) in | ||
| let zs = List.fold_left (fun acc (s, s') -> (s, mk_symbol srk `TyInt) :: (s', mk_symbol srk `TyInt) :: acc) [] array_symbols in | ||
| let j = mk_symbol srk ~name:"j" `TyInt in | ||
| let j' = mk_symbol srk ~name:"j'" `TyInt in | ||
| let projected_formula = mk_and srk (f :: (mk_eq srk (mk_const srk j) (mk_const srk j')) :: (List.map (fun (a, z) -> mk_eq srk (mk_select srk (mk_const srk a) (mk_const srk j)) (mk_const srk z)) zs)) in | ||
| let projected_formula = Symbol.Set.fold (fun s acc-> | ||
| if typ_symbol srk s = `TyArr then mk_exists_const srk s acc else acc | ||
| ) (Syntax.symbols projected_formula) projected_formula in | ||
|
|
||
| let existentials, forall, skol = bubble_sym srk projected_formula in | ||
| let with_universal = mk_forall_const srk forall skol in | ||
|
|
||
| let with_everything = List.fold_left (fun acc s -> mk_exists_const srk s acc) with_universal existentials in | ||
|
|
||
| let eliminated = SrkZ3.qe srk with_everything in | ||
|
|
||
| let tf'_symbols = (j, j') :: List.map (fun (s, s') -> if (typ_symbol srk s = `TyArr) then (((List.find (fun (sym, _) -> sym = s') zs) |> snd), ((List.find (fun (sym, _) -> sym = s') zs) |> snd)) else (s, s')) (TransitionFormula.symbols tf) in | ||
| let tf' = TransitionFormula.make ~exists:(fun sym -> (not (List.exists (fun (s, s') -> s = sym || s' = sym) tf'_symbols)) && TransitionFormula.exists tf sym) eliminated tf'_symbols in | ||
| let solver' = Solver.make srk tf' in | ||
|
|
||
| let exponentiated = e solver' k in | ||
|
|
||
| let ret = substitute_sym srk (fun s -> | ||
| match List.find_opt (fun (_, z) -> s = z) zs with | ||
| | Some (a, _) -> mk_select srk (mk_const srk a) (mk_const srk j) | ||
| | None -> mk_const srk s | ||
| ) exponentiated in | ||
| let ret = substitute_sym srk (fun s -> | ||
| if s = j' then mk_const srk j else mk_const srk s) ret in | ||
|
|
||
| mk_forall_const srk j ret | ||
|
|
||
|
|
||
| (* [map_elim ctx f] takes a formula in the array skolem fragment and returns an equivalent formula over numerical variables. *) | ||
| let map_elim (srk : 'a context) (f : 'a formula) : 'a formula = | ||
| let syms, forall, f = bubble_sym srk f in | ||
| let f = mk_forall_const srk forall f in | ||
|
|
||
| let f = List.fold_left (fun acc sym -> mk_exists_const srk sym acc) f syms in | ||
| f | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| open Syntax | ||
|
|
||
|
|
||
| (** [array_exponentiate ctx exp_op] takes an exponentiation operation [exp_op] | ||
| , and returns an exponentiation operator which numerically abstracts the array variables | ||
| in the formula, applies exp_op, then reintroduces the array variables | ||
| *) | ||
| val array_exponentiate : | ||
| 'a context -> | ||
| 'a Iteration.exp_op -> | ||
| ('a TransitionFormula.t -> 'a arith_term -> 'a formula) | ||
|
|
||
|
|
||
| (** [map_elim ctx formula] computes an equisatisfiable formula to [formula] | ||
|
zkincaid marked this conversation as resolved.
|
||
| in which all array variables are replaced with numerical abstractions. | ||
| *) | ||
| val map_elim : 'a context -> 'a formula -> 'a formula | ||
|
nikhilpim marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.