Skip to content

Comments

Map Elimination Implementation#74

Open
nikhilpim wants to merge 18 commits intozkincaid:flintfrom
nikhilpim:array-pr
Open

Map Elimination Implementation#74
nikhilpim wants to merge 18 commits intozkincaid:flintfrom
nikhilpim:array-pr

Conversation

@nikhilpim
Copy link

array_lift.ml - implementation of the "bubble" algorithm for map elimination, and two handlers: map_elim which eliminates array variables from a formula and array_exponentiate which lifts exponentiation operators to work over arrays.
test_arraylist.ml - some initial tests for these functions
transitionFormula - added a printing function for transition formulas.

let ae = replace_access a index env qo in
mk_ite srk (mk_eq srk store_index index) value ae
(* replace_at recurses over arith_terms, performing substitution *)
and replace_at (t : 'a arith_term) (env : (symbol Env.t)) (qo : int) : 'a arith_term =
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is: rewrite srk ~up:(function Select(arr, index) -> replace_access arr index env qo | x -> x) t

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some details in the handling of Var and Ite that aren't so easy to express as a rewriter - I think it's more readable to leave everything explicit.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the issues?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, Var (i, t) needs to search in the environment to find the relevant skolem constant, which is easy to put in a rewriter.

But Ite (f, l, r) needs to apply replace (for formulas) to f and replace_at to l and r. I'm not sure exactly how to put that as a rewriter.

Does changing the implementation of replace_at to use rewriter buy us anything in terms of performance? Because otherwise we'll have a match statement regardless.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that the purpose of replace/replace_at is to replace every array selection term according to replace_access (but the reason they're two separate functions is that replace operates on formulas and replace_at operates on terms). If that's the case,
fun expr -> rewrite srk ~up:(function Select(arr, index) -> replace_access arr index env qo | x -> x) expr
does the same thing, and works for both formulas and terms.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true, but it is also responsible for replacing existential variables with the new skolem constants that were introduced in go. The logic for this is in the Var case of replace_at and the Exists case of replace. This is difficult to implement as a rewriter because we need additional context about the quantifier offset (qo) of variables we should replace versus leave unchanged.

@nikhilpim
Copy link
Author

@zkincaid I'm done making edits based on your review - it's ready to be looked over again. All tests pass.

*)
let bubble_sym (srk : 'a context) (form : 'a formula) : (symbol list * (symbol option) * 'a formula) =
(* rep_map : arr_var -> (index_term -> var) *)
let (rep_map : (symbol, ('a arith_term, symbol) Hashtbl.t) Hashtbl.t) = Hashtbl.create 16 in
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arith_term should not be used as a key for hashtbl -- use Expr.HT (this gives constant-time hashing).
(Or use Expr.ExprMemo to memoize get_or_create_replacement)

let ae = replace_access a index env qo in
mk_ite srk (mk_eq srk store_index index) value ae
(* replace_at recurses over arith_terms, performing substitution *)
and replace_at (t : 'a arith_term) (env : (symbol Env.t)) (qo : int) : 'a arith_term =
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the issues?

| `Quantify (`Forall, _, typ, body) ->
assert (typ = `TyInt);
let (body) = replace (Env.push forall_symbol env) 0 body in
let functional_correctness = (Hashtbl.fold (fun array_symbol m (acc) ->
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functional consistency

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic is not correct here. Functional consistency axioms for the array a need to be inserted at the existential for a, not the universal quantifier.

Test case that should reveal the problem: exists n,m : int. exists a : array. n = m /\ a[n] != a[m]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That test case was unproblematic - after applying map_elim it was still UNSAT.

Because our algorithm automatically assumes a universal at any atom, we treat the above test case as exists n,m : int. exists a : array. forall i. n = m /\ a[n] != a[m].

The result of calling map elim looks like:

exists b, n, m, a_n, a_m. forall i. exists a_i . n = m /\ ((b=0 /\ a_n < a_m /\ FC) / (b = 1 /\ a_m < a_n /\ FC))

where:

FC: (m = i => a_m = a_i) /\ (n = i => a_n = a_i)

And this formula is UNSAT as expected.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see -- atomic formulas are treated as being universally quantified per line 140. But then shouldn' this be treated the same as

exists n,m : int. exists a : array. (forall i. n = m) /\ (forall i. a[n] != a[m]).

(which should also give the incorrect result?).

Is the existentially-quantified "b" variable really introduced for this example? It shouldn't be -- if the formula is unsat because it's asserting both b=0 and b=1, then it's masking the problem.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it does get treated as (forall i. n = m) /\ (forall i. a[n] != a[m]), but this gets "bubbled" to forall i. (n = m /\ a[n] != a[m]) per one of the rewrite rules. It is true that this results in the functional consistency axioms being duplicated in each conjuct.

The FC axioms need to appear inside the forall, right? Because the universal "i" is needed within the definition of FC.

The existential b gets introduced because a[n] != a[m] gets rewritten to a[n] < a[m] / a[n] > a[m] to get rid of the negation.

@nikhilpim
Copy link
Author

@zkincaid Finished changes as discussed in meeting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants