|
1 (* Title: Tools/Argo/argo_cc.ML |
|
2 Author: Sascha Boehme |
|
3 Author: Dmitriy Traytel and Matthias Franze, TU Muenchen |
|
4 |
|
5 Equality reasoning based on congurence closure. It features: |
|
6 |
|
7 * congruence closure for any term that participates in equalities |
|
8 * support for predicates |
|
9 |
|
10 These features might be added: |
|
11 |
|
12 * caching of explanations while building proofs to obtain shorter proofs |
|
13 and faster proof checking |
|
14 * propagating relevant merges of equivalence classes to all other theory solvers |
|
15 * propagating new relevant negated equalities to all other theory solvers |
|
16 * creating lemma "f ~= g | a ~= b | f a = g b" for asserted negated equalities |
|
17 between "f a" and "g b" (dynamic ackermannization) |
|
18 |
|
19 The implementation is inspired by: |
|
20 |
|
21 Robert Nieuwenhuis and Albert Oliveras. Fast Congruence Closure and |
|
22 Extensions. In Information and Computation, volume 205(4), |
|
23 pages 557-580, 2007. |
|
24 |
|
25 Harald Ganzinger, George Hagen, Robert Nieuwenhuis, Albert Oliveras, |
|
26 Cesare Tinelli. DPLL(T): Fast decision procedures. In Lecture Notes in |
|
27 Computer Science, volume 3114, pages 175-188. Springer, 2004. |
|
28 *) |
|
29 |
|
30 signature ARGO_CC = |
|
31 sig |
|
32 (* context *) |
|
33 type context |
|
34 val context: context |
|
35 |
|
36 (* simplification *) |
|
37 val simplify: Argo_Rewr.context -> Argo_Rewr.context |
|
38 |
|
39 (* enriching the context *) |
|
40 val add_atom: Argo_Term.term -> context -> Argo_Lit.literal option * context |
|
41 |
|
42 (* main operations *) |
|
43 val assume: Argo_Common.literal -> context -> Argo_Lit.literal Argo_Common.implied * context |
|
44 val check: context -> Argo_Lit.literal Argo_Common.implied * context |
|
45 val explain: Argo_Lit.literal -> context -> (Argo_Cls.clause * context) option |
|
46 val add_level: context -> context |
|
47 val backtrack: context -> context |
|
48 end |
|
49 |
|
50 structure Argo_Cc: ARGO_CC = |
|
51 struct |
|
52 |
|
53 (* tables indexed by pairs of terms *) |
|
54 |
|
55 val term2_ord = prod_ord Argo_Term.term_ord Argo_Term.term_ord |
|
56 |
|
57 structure Argo_Term2tab = Table(type key = Argo_Term.term * Argo_Term.term val ord = term2_ord) |
|
58 |
|
59 |
|
60 (* equality certificates *) |
|
61 |
|
62 (* |
|
63 The solver keeps assumed equalities to produce explanations later on. |
|
64 |
|
65 A flat equality (lp, (t1, t2)) consists of the assumed literal and its proof |
|
66 as well as the terms t1 and t2 that are assumed to be equal. The literal expresses |
|
67 the equality t1 = t2. |
|
68 |
|
69 A congruence equality (t1, t2) is an equality t1 = t2 where both terms are |
|
70 applications (f a) and (g b). |
|
71 |
|
72 A symmetric equality eq is a marker for applying the symmetry rule to eq. |
|
73 *) |
|
74 |
|
75 datatype eq = |
|
76 Flat of Argo_Common.literal * (Argo_Term.term * Argo_Term.term) | |
|
77 Cong of Argo_Term.term * Argo_Term.term | |
|
78 Symm of eq |
|
79 |
|
80 fun dest_eq (Flat (_, tp)) = tp |
|
81 | dest_eq (Cong tp) = tp |
|
82 | dest_eq (Symm eq) = swap (dest_eq eq) |
|
83 |
|
84 fun symm (Symm eq) = eq |
|
85 | symm eq = Symm eq |
|
86 |
|
87 fun negate (Flat ((lit, p), tp)) = Flat ((Argo_Lit.negate lit, p), tp) |
|
88 | negate (Cong tp) = Cong tp |
|
89 | negate (Symm eq) = Symm (negate eq) |
|
90 |
|
91 fun dest_app (Argo_Term.T (_, Argo_Expr.App, [t1, t2])) = (t1, t2) |
|
92 | dest_app _ = raise Fail "bad application" |
|
93 |
|
94 |
|
95 (* context *) |
|
96 |
|
97 (* |
|
98 Each representative keeps track of the yet unimplied atoms in which this any member of |
|
99 this representative's equivalence class occurs. An atom is either a list of equalities |
|
100 between two terms, a list of predicates or a certificate. The certificate denotes that |
|
101 this equivalence class contains already implied predicates, and the literal accompanying |
|
102 the certificate specifies the polarity of these predicates. |
|
103 *) |
|
104 |
|
105 datatype atoms = |
|
106 Eqs of (Argo_Term.term * Argo_Term.term) list | |
|
107 Preds of Argo_Term.term list | |
|
108 Cert of Argo_Common.literal |
|
109 |
|
110 (* |
|
111 Each representative has an associated ritem that contains the members of the |
|
112 equivalence class, the yet unimplied atoms and further information. |
|
113 *) |
|
114 |
|
115 type ritem = { |
|
116 size: int, (* the size of the equivalence class *) |
|
117 class: Argo_Term.term list, (* the equivalence class as a list of distinct terms *) |
|
118 occs: Argo_Term.term list, (* a list of all application terms in which members of |
|
119 the equivalence class occur either as function or as argument *) |
|
120 neqs: (Argo_Term.term * eq) list, (* a list of terms from disjoint equivalence classes, |
|
121 for each term of this list there is a certificate of a negated equality that is |
|
122 required to explain why the equivalence classes are disjoint *) |
|
123 atoms: atoms} (* the atoms of the representative *) |
|
124 |
|
125 type repr = Argo_Term.term Argo_Termtab.table |
|
126 type rdata = ritem Argo_Termtab.table |
|
127 type apps = Argo_Term.term Argo_Term2tab.table |
|
128 type trace = (Argo_Term.term * eq) Argo_Termtab.table |
|
129 |
|
130 type context = { |
|
131 repr: repr, (* a table mapping terms to their representatives *) |
|
132 rdata: rdata, (* a table mapping representatives to their ritems *) |
|
133 apps: apps, (* a table mapping a function and an argument to their application *) |
|
134 trace: trace, (* the proof forest used to trace assumed and implied equalities *) |
|
135 prf: Argo_Proof.context, (* the proof context *) |
|
136 back: (repr * rdata * apps * trace) list} (* backtracking information *) |
|
137 |
|
138 fun mk_context repr rdata apps trace prf back: context = |
|
139 {repr=repr, rdata=rdata, apps=apps, trace=trace, prf=prf, back=back} |
|
140 |
|
141 val context = |
|
142 mk_context Argo_Termtab.empty Argo_Termtab.empty Argo_Term2tab.empty Argo_Termtab.empty |
|
143 Argo_Proof.cc_context [] |
|
144 |
|
145 fun repr_of repr t = the_default t (Argo_Termtab.lookup repr t) |
|
146 fun repr_of' ({repr, ...}: context) = repr_of repr |
|
147 fun put_repr t r = Argo_Termtab.update (t, r) |
|
148 |
|
149 fun mk_ritem size class occs neqs atoms: ritem = |
|
150 {size=size, class=class, occs=occs, neqs=neqs, atoms=atoms} |
|
151 |
|
152 fun as_ritem t = mk_ritem 1 [t] [] [] (Eqs []) |
|
153 fun as_pred_ritem t = mk_ritem 1 [t] [] [] (Preds [t]) |
|
154 fun gen_ritem_of mk rdata r = the_default (mk r) (Argo_Termtab.lookup rdata r) |
|
155 fun ritem_of rdata = gen_ritem_of as_ritem rdata |
|
156 fun ritem_of_pred rdata = gen_ritem_of as_pred_ritem rdata |
|
157 fun ritem_of' ({rdata, ...}: context) = ritem_of rdata |
|
158 fun put_ritem r ri = Argo_Termtab.update (r, ri) |
|
159 |
|
160 fun add_occ r occ = Argo_Termtab.map_default (r, as_ritem r) |
|
161 (fn {size, class, occs, neqs, atoms}: ritem => mk_ritem size class (occ :: occs) neqs atoms) |
|
162 |
|
163 fun put_atoms atoms ({size, class, occs, neqs, ...}: ritem) = mk_ritem size class occs neqs atoms |
|
164 |
|
165 fun add_eq_atom r atom = Argo_Termtab.map_default (r, as_ritem r) |
|
166 (fn ri as {atoms=Eqs atoms, ...}: ritem => put_atoms (Eqs (atom :: atoms)) ri |
|
167 | ri => put_atoms (Eqs [atom]) ri) |
|
168 |
|
169 fun lookup_app apps tp = Argo_Term2tab.lookup apps tp |
|
170 fun put_app tp app = Argo_Term2tab.update_new (tp, app) |
|
171 |
|
172 |
|
173 (* traces for explanations *) |
|
174 |
|
175 (* |
|
176 Assumed and implied equalities are collected in a proof forest for being able to |
|
177 produce explanations. For each equivalence class there is one proof tree. The |
|
178 equality certificates are oriented towards a root term, that is not necessarily |
|
179 the representative of the equivalence class. |
|
180 *) |
|
181 |
|
182 (* |
|
183 Whenever two equivalence classes are merged due to an equality t1 = t2, the shorter |
|
184 of the two paths, either from t1 to its root or t2 to its root, is re-oriented such |
|
185 that the relevant ti becomes the new root of its tree. Then, a new edge between ti |
|
186 and the other term of the equality t1 = t2 is added to connect the two proof trees. |
|
187 *) |
|
188 |
|
189 fun depth_of trace t = |
|
190 (case Argo_Termtab.lookup trace t of |
|
191 NONE => 0 |
|
192 | SOME (t', _) => 1 + depth_of trace t') |
|
193 |
|
194 fun reorient t trace = |
|
195 (case Argo_Termtab.lookup trace t of |
|
196 NONE => trace |
|
197 | SOME (t', eq) => Argo_Termtab.update (t', (t, symm eq)) (reorient t' trace)) |
|
198 |
|
199 fun new_edge from to eq trace = Argo_Termtab.update (from, (to, eq)) (reorient from trace) |
|
200 |
|
201 fun with_shortest f (t1, t2) eq trace = |
|
202 (if depth_of trace t1 <= depth_of trace t2 then f t1 t2 eq else f t2 t1 (symm eq)) trace |
|
203 |
|
204 fun add_edge eq trace = with_shortest new_edge (dest_eq eq) eq trace |
|
205 |
|
206 (* |
|
207 To produce an explanation that t1 and t2 are equal, the paths to their root are |
|
208 extracted from the proof forest. Common ancestors in both paths are dropped. |
|
209 *) |
|
210 |
|
211 fun path_to_root trace path t = |
|
212 (case Argo_Termtab.lookup trace t of |
|
213 NONE => (t, path) |
|
214 | SOME (t', _) => path_to_root trace (t :: path) t') |
|
215 |
|
216 fun drop_common root (t1 :: path1) (t2 :: path2) = |
|
217 if Argo_Term.eq_term (t1, t2) then drop_common t1 path1 path2 else root |
|
218 | drop_common root _ _ = root |
|
219 |
|
220 fun common_ancestor trace t1 t2 = |
|
221 let val ((root, path1), (_, path2)) = apply2 (path_to_root trace []) (t1, t2) |
|
222 in drop_common root path1 path2 end |
|
223 |
|
224 (* |
|
225 The proof of an assumed literal is typically a hypothesis. If the assumed literal is |
|
226 already known to be a unit literal, then there is already a proof for it. |
|
227 *) |
|
228 |
|
229 fun proof_of (lit, NONE) lits prf = |
|
230 (insert Argo_Lit.eq_lit (Argo_Lit.negate lit) lits, Argo_Proof.mk_hyp lit prf) |
|
231 | proof_of (_, SOME p) lits prf = (lits, (p, prf)) |
|
232 |
|
233 (* |
|
234 The explanation of equality between two terms t1 and t2 is computed based on the |
|
235 paths from t1 and t2 to their common ancestor t in the proof forest. For each of |
|
236 the two paths, a transitive proof of equality t1 = t and t = t2 is constructed, |
|
237 such that t1 = t2 follows by transitivity. |
|
238 |
|
239 Each edge of the paths denotes an assumed or implied equality. Implied equalities |
|
240 might be due to congruences (f a = g b) for which the equalities f = g and a = b |
|
241 need to be explained recursively. |
|
242 *) |
|
243 |
|
244 fun mk_eq_proof trace t1 t2 lits prf = |
|
245 if Argo_Term.eq_term (t1, t2) then (lits, Argo_Proof.mk_refl t1 prf) |
|
246 else |
|
247 let |
|
248 val root = common_ancestor trace t1 t2 |
|
249 val (lits, (p1, prf)) = trans_proof I I trace t1 root lits prf |
|
250 val (lits, (p2, prf)) = trans_proof swap symm trace t2 root lits prf |
|
251 in (lits, Argo_Proof.mk_trans p1 p2 prf) end |
|
252 |
|
253 and trans_proof sw sy trace t root lits prf = |
|
254 if Argo_Term.eq_term (t, root) then (lits, Argo_Proof.mk_refl t prf) |
|
255 else |
|
256 (case Argo_Termtab.lookup trace t of |
|
257 NONE => raise Fail "bad trace" |
|
258 | SOME (t', eq) => |
|
259 let |
|
260 val (lits, (p1, prf)) = proof_step trace (sy eq) lits prf |
|
261 val (lits, (p2, prf)) = trans_proof sw sy trace t' root lits prf |
|
262 in (lits, uncurry Argo_Proof.mk_trans (sw (p1, p2)) prf) end) |
|
263 |
|
264 and proof_step _ (Flat (cert, _)) lits prf = proof_of cert lits prf |
|
265 | proof_step trace (Cong tp) lits prf = |
|
266 let |
|
267 val ((t1, t2), (u1, u2)) = apply2 dest_app tp |
|
268 val (lits, (p1, prf)) = mk_eq_proof trace t1 u1 lits prf |
|
269 val (lits, (p2, prf)) = mk_eq_proof trace t2 u2 lits prf |
|
270 in (lits, Argo_Proof.mk_cong p1 p2 prf) end |
|
271 | proof_step trace (Symm eq) lits prf = |
|
272 proof_step trace eq lits prf ||> uncurry Argo_Proof.mk_symm |
|
273 |
|
274 (* |
|
275 All clauses produced by a theory solver are expected to be a lemma. |
|
276 The lemma proof must hence be the last proof step. |
|
277 *) |
|
278 |
|
279 fun close_proof lit lits (p, prf) = (lit :: lits, Argo_Proof.mk_lemma [lit] p prf) |
|
280 |
|
281 (* |
|
282 The explanation for the equality of t1 and t2 used the above algorithm. |
|
283 *) |
|
284 |
|
285 fun explain_eq lit t1 t2 ({repr, rdata, apps, trace, prf, back}: context) = |
|
286 let val (lits, (p, prf)) = mk_eq_proof trace t1 t2 [] prf |-> close_proof lit |
|
287 in ((lits, p), mk_context repr rdata apps trace prf back) end |
|
288 |
|
289 (* |
|
290 The explanation that t1 and t2 are distinct uses the negated equality u1 ~= u2 that |
|
291 explains why the equivalence class containing t1 and u1 and the equivalence class |
|
292 containing t2 and u2 are disjoint. The explanations for t1 = u1 and u2 = t2 are |
|
293 constructed using the above algorithm. By transitivity, it follows that t1 ~= t2. |
|
294 *) |
|
295 |
|
296 fun finish_proof (Flat ((lit, _), _)) lits p prf = close_proof lit lits (p, prf) |
|
297 | finish_proof (Cong _) _ _ _ = raise Fail "bad equality" |
|
298 | finish_proof (Symm eq) lits p prf = Argo_Proof.mk_symm p prf |-> finish_proof eq lits |
|
299 |
|
300 fun explain_neq eq eq' ({repr, rdata, apps, trace, prf, back}: context) = |
|
301 let |
|
302 val (t1, t2) = dest_eq eq |
|
303 val (u1, u2) = dest_eq eq' |
|
304 |
|
305 val (lits, (p, prf)) = proof_step trace eq' [] prf |
|
306 val (lits, (p1, prf)) = mk_eq_proof trace t1 u1 lits prf |
|
307 val (lits, (p2, prf)) = mk_eq_proof trace u2 t2 lits prf |
|
308 val (lits, (p, prf)) = |
|
309 Argo_Proof.mk_trans p p2 prf |-> Argo_Proof.mk_trans p1 |-> finish_proof eq lits |
|
310 in ((lits, p), mk_context repr rdata apps trace prf back) end |
|
311 |
|
312 |
|
313 (* propagating new equalities *) |
|
314 |
|
315 exception CONFLICT of Argo_Cls.clause * context |
|
316 |
|
317 (* |
|
318 comment missing |
|
319 *) |
|
320 |
|
321 fun same_repr repr r (t, _) = Argo_Term.eq_term (r, repr_of repr t) |
|
322 |
|
323 fun has_atom rdata r eq = |
|
324 (case #atoms (ritem_of rdata r) of |
|
325 Eqs eqs => member (Argo_Term.eq_term o snd) eqs eq |
|
326 | _ => false) |
|
327 |
|
328 fun add_implied mk_lit repr rdata r neqs (atom as (t, eq)) (eqs, ls) = |
|
329 let val r' = repr_of repr t |
|
330 in |
|
331 if Argo_Term.eq_term (r, r') then (eqs, insert Argo_Lit.eq_lit (mk_lit eq) ls) |
|
332 else if exists (same_repr repr r') neqs andalso has_atom rdata r' eq then |
|
333 (eqs, Argo_Lit.Neg eq :: ls) |
|
334 else (atom :: eqs, ls) |
|
335 end |
|
336 |
|
337 (* |
|
338 comment missing |
|
339 *) |
|
340 |
|
341 fun copy_occ repr app (eqs, occs, apps) = |
|
342 let val rp = apply2 (repr_of repr) (dest_app app) |
|
343 in |
|
344 (case lookup_app apps rp of |
|
345 SOME app' => (Cong (app, app') :: eqs, occs, apps) |
|
346 | NONE => (eqs, app :: occs, put_app rp app apps)) |
|
347 end |
|
348 |
|
349 (* |
|
350 comment missing |
|
351 *) |
|
352 |
|
353 fun add_lits (Argo_Lit.Pos _, _) = fold (cons o Argo_Lit.Pos) |
|
354 | add_lits (Argo_Lit.Neg _, _) = fold (cons o Argo_Lit.Neg) |
|
355 |
|
356 fun join_atoms f (Eqs eqs1) (Eqs eqs2) ls = f eqs1 eqs2 ls |
|
357 | join_atoms _ (Preds ts1) (Preds ts2) ls = (Preds (union Argo_Term.eq_term ts1 ts2), ls) |
|
358 | join_atoms _ (Preds ts) (Cert lp) ls = (Cert lp, add_lits lp ts ls) |
|
359 | join_atoms _ (Cert lp) (Preds ts) ls = (Cert lp, add_lits lp ts ls) |
|
360 | join_atoms _ (Cert lp) (Cert _) ls = (Cert lp, ls) |
|
361 | join_atoms _ _ _ _ = raise Fail "bad atoms" |
|
362 |
|
363 (* |
|
364 comment missing |
|
365 *) |
|
366 |
|
367 fun join r1 ri1 r2 ri2 eq (eqs, ls, {repr, rdata, apps, trace, prf, back}: context) = |
|
368 let |
|
369 val {size=size1, class=class1, occs=occs1, neqs=neqs1, atoms=atoms1}: ritem = ri1 |
|
370 val {size=size2, class=class2, occs=occs2, neqs=neqs2, atoms=atoms2}: ritem = ri2 |
|
371 |
|
372 val repr = fold (fn t => put_repr t r1) class2 repr |
|
373 val class = fold cons class2 class1 |
|
374 val (eqs, occs, apps) = fold (copy_occ repr) occs2 (eqs, occs1, apps) |
|
375 val trace = add_edge eq trace |
|
376 val neqs = AList.merge Argo_Term.eq_term (K true) (neqs1, neqs2) |
|
377 fun add r neqs = fold (add_implied Argo_Lit.Pos repr rdata r neqs) |
|
378 fun adds eqs1 eqs2 ls = ([], ls) |> add r2 neqs2 eqs1 |> add r1 neqs1 eqs2 |>> Eqs |
|
379 val (atoms, ls) = join_atoms adds atoms1 atoms2 ls |
|
380 (* TODO: make sure that all implied literals are propagated *) |
|
381 val rdata = put_ritem r1 (mk_ritem (size1 + size2) class occs neqs atoms) rdata |
|
382 in (eqs, ls, mk_context repr rdata apps trace prf back) end |
|
383 |
|
384 (* |
|
385 comment missing |
|
386 *) |
|
387 |
|
388 fun find_neq ({repr, ...}: context) ({neqs, ...}: ritem) r = find_first (same_repr repr r) neqs |
|
389 |
|
390 fun check_join (r1, r2) (ri1, ri2) eq (ecx as (_, _, cx)) = |
|
391 (case find_neq cx ri2 r1 of |
|
392 SOME (_, eq') => raise CONFLICT (explain_neq (negate (symm eq)) eq' cx) |
|
393 | NONE => |
|
394 (case find_neq cx ri1 r2 of |
|
395 SOME (_, eq') => raise CONFLICT (explain_neq (negate eq) eq' cx) |
|
396 | NONE => join r1 ri1 r2 ri2 eq ecx)) |
|
397 |
|
398 (* |
|
399 comment missing |
|
400 *) |
|
401 |
|
402 fun with_max_class f (rp as (r1, r2)) (rip as (ri1: ritem, ri2: ritem)) eq = |
|
403 if #size ri1 >= #size ri2 then f rp rip eq else f (r2, r1) (ri2, ri1) (symm eq) |
|
404 |
|
405 (* |
|
406 comment missing |
|
407 *) |
|
408 |
|
409 fun propagate ([], ls, cx) = (rev ls, cx) |
|
410 | propagate (eq :: eqs, ls, cx) = |
|
411 let val rp = apply2 (repr_of' cx) (dest_eq eq) |
|
412 in |
|
413 if Argo_Term.eq_term rp then propagate (eqs, ls, cx) |
|
414 else propagate (with_max_class check_join rp (apply2 (ritem_of' cx) rp) eq (eqs, ls, cx)) |
|
415 end |
|
416 |
|
417 fun without lit (lits, cx) = (Argo_Common.Implied (remove Argo_Lit.eq_lit lit lits), cx) |
|
418 |
|
419 fun flat_merge (lp as (lit, _)) eq cx = without lit (propagate ([Flat (lp, eq)], [], cx)) |
|
420 handle CONFLICT (cls, cx) => (Argo_Common.Conflict cls, cx) |
|
421 |
|
422 (* |
|
423 comment missing |
|
424 *) |
|
425 |
|
426 fun app_merge app tp (cx as {repr, rdata, apps, trace, prf, back}: context) = |
|
427 let val rp as (r1, r2) = apply2 (repr_of repr) tp |
|
428 in |
|
429 (case lookup_app apps rp of |
|
430 SOME app' => |
|
431 (case propagate ([Cong (app, app')], [], cx) of |
|
432 ([], cx) => cx |
|
433 | _ => raise Fail "bad application merge") |
|
434 | NONE => |
|
435 let val rdata = add_occ r1 app (add_occ r2 app rdata) |
|
436 in mk_context repr rdata (put_app rp app apps) trace prf back end) |
|
437 end |
|
438 |
|
439 (* |
|
440 A negated equality between t1 and t2 is only recorded if t1 and t2 are not already known |
|
441 to belong to the same class. In that case, a conflict is raised with an explanation |
|
442 why t1 and t2 are equal. Otherwise, the classes of t1 and t2 are marked as disjoint by |
|
443 storing the negated equality in the ritems of t1's and t2's representative. All equalities |
|
444 between terms of t1's and t2's class are implied as negated equalities. Those equalities |
|
445 are found in the ritems of t1's and t2's representative. |
|
446 *) |
|
447 |
|
448 fun note_neq eq (r1, r2) (t1, t2) ({repr, rdata, apps, trace, prf, back}: context) = |
|
449 let |
|
450 val {size=size1, class=class1, occs=occs1, neqs=neqs1, atoms=atoms1}: ritem = ritem_of rdata r1 |
|
451 val {size=size2, class=class2, occs=occs2, neqs=neqs2, atoms=atoms2}: ritem = ritem_of rdata r2 |
|
452 |
|
453 fun add r (Eqs eqs) ls = fold (add_implied Argo_Lit.Neg repr rdata r []) eqs ([], ls) |>> Eqs |
|
454 | add _ _ _ = raise Fail "bad negated equality between predicates" |
|
455 val ((atoms1, atoms2), ls) = [] |> add r2 atoms1 ||>> add r1 atoms2 |
|
456 val ri1 = mk_ritem size1 class1 occs1 ((t2, eq) :: neqs1) atoms1 |
|
457 val ri2 = mk_ritem size2 class2 occs2 ((t1, symm eq) :: neqs2) atoms2 |
|
458 in (ls, mk_context repr (put_ritem r1 ri1 (put_ritem r2 ri2 rdata)) apps trace prf back) end |
|
459 |
|
460 fun flat_neq (lp as (lit, _)) (tp as (t1, t2)) cx = |
|
461 let val rp = apply2 (repr_of' cx) tp |
|
462 in |
|
463 if Argo_Term.eq_term rp then |
|
464 let val (cls, cx) = explain_eq (Argo_Lit.negate lit) t1 t2 cx |
|
465 in (Argo_Common.Conflict cls, cx) end |
|
466 else without lit (note_neq (Flat (lp, tp)) rp tp cx) |
|
467 end |
|
468 |
|
469 |
|
470 (* simplification *) |
|
471 |
|
472 (* |
|
473 Only equalities are subject to normalizations. An equality between two expressions e1 and e2 |
|
474 is normalized, if e1 is less than e2 based on the expression ordering. If e1 and e2 are |
|
475 syntactically equal, the equality between these two expressions is normalized to the true |
|
476 expression. |
|
477 *) |
|
478 |
|
479 fun norm_eq env = |
|
480 let val e1 = Argo_Rewr.get env 1 and e2 = Argo_Rewr.get env 2 |
|
481 in |
|
482 (case Argo_Expr.expr_ord (e1, e2) of |
|
483 EQUAL => SOME (Argo_Proof.Rewr_Eq_Refl, Argo_Rewr.E Argo_Expr.true_expr) |
|
484 | LESS => NONE |
|
485 | GREATER => SOME (Argo_Proof.Rewr_Eq_Symm, Argo_Rewr.E (Argo_Expr.mk_eq e2 e1))) |
|
486 end |
|
487 |
|
488 val simplify = Argo_Rewr.func "(eq (? 1) (? 2))" norm_eq |
|
489 |
|
490 |
|
491 (* declaring atoms *) |
|
492 |
|
493 (* |
|
494 Only a genuinely new equality term t for the equality "t1 = t2" is added. If t1 and t2 belong |
|
495 to the same equality class or if the classes of t1 and t2 are known to be disjoint, the |
|
496 respective literal is returned together with an unmodified context. |
|
497 *) |
|
498 |
|
499 fun add_eq_term t t1 t2 (rp as (r1, r2)) (cx as {repr, rdata, apps, trace, prf, back}: context) = |
|
500 if Argo_Term.eq_term rp then (SOME (Argo_Lit.Pos t), cx) |
|
501 else if is_some (find_neq cx (ritem_of rdata r1) r2) then (SOME (Argo_Lit.Neg t), cx) |
|
502 else |
|
503 let val rdata = add_eq_atom r1 (t2, t) (add_eq_atom r2 (t1, t) rdata) |
|
504 in (NONE, mk_context repr rdata apps trace prf back) end |
|
505 |
|
506 (* |
|
507 Only a genuinely new predicate t, which is an application "t1 t2", is added. |
|
508 If there is a predicate that is known to be congruent to the representatives of t1 and t2, |
|
509 and that predicate or its negation has already been assummed, the respective literal of t |
|
510 is returned together with an unmodified context. |
|
511 *) |
|
512 |
|
513 fun add_pred_term t rp (cx as {repr, rdata, apps, trace, prf, back}: context) = |
|
514 (case lookup_app apps rp of |
|
515 NONE => (NONE, mk_context repr (put_ritem t (as_pred_ritem t) rdata) apps trace prf back) |
|
516 | SOME app => |
|
517 (case `(ritem_of_pred rdata) (repr_of repr app) of |
|
518 ({atoms=Cert (Argo_Lit.Pos _, _), ...}: ritem, _) => (SOME (Argo_Lit.Pos t), cx) |
|
519 | ({atoms=Cert (Argo_Lit.Neg _, _), ...}: ritem, _) => (SOME (Argo_Lit.Neg t), cx) |
|
520 | (ri as {atoms=Preds ts, ...}: ritem, r) => |
|
521 let val rdata = put_ritem r (put_atoms (Preds (t :: ts)) ri) rdata |
|
522 in (NONE, mk_context repr rdata apps trace prf back) end |
|
523 | ({atoms=Eqs _, ...}: ritem, _) => raise Fail "bad predicate")) |
|
524 |
|
525 (* |
|
526 For each term t that is an application "t1 t2", the reflexive equality t = t1 t2 is added |
|
527 to the context. This is required for propagations of congruences. |
|
528 *) |
|
529 |
|
530 fun flatten (t as Argo_Term.T (_, Argo_Expr.App, [t1, t2])) cx = |
|
531 flatten t1 (flatten t2 (app_merge t (t1, t2) cx)) |
|
532 | flatten _ cx = cx |
|
533 |
|
534 (* |
|
535 Atoms to be added to the context must either be an equality "t1 = t2" or |
|
536 an application "t1 t2" (a predicate). Besides adding the equality or the application, |
|
537 reflexive equalities for for all applications in the terms t1 and t2 are added. |
|
538 *) |
|
539 |
|
540 fun add_atom (t as Argo_Term.T (_, Argo_Expr.Eq, [t1, t2])) cx = |
|
541 add_eq_term t t1 t2 (apply2 (repr_of' cx) (t1, t2)) (flatten t1 (flatten t2 cx)) |
|
542 | add_atom (t as Argo_Term.T (_, Argo_Expr.App, [t1, t2])) cx = |
|
543 let val cx = flatten t1 (flatten t2 (app_merge t (t1, t2) cx)) |
|
544 in add_pred_term t (apply2 (repr_of' cx) (t1, t2)) cx end |
|
545 | add_atom _ cx = (NONE, cx) |
|
546 |
|
547 |
|
548 (* assuming external knowledge *) |
|
549 |
|
550 (* |
|
551 Assuming a predicate r replaces all predicate atoms of r's ritem with the assumed certificate. |
|
552 The predicate atoms are implied, either with positive or with negative polarity based on |
|
553 the assumption. |
|
554 |
|
555 There must not be a certificate for r since otherwise r would have been assumed before already. |
|
556 *) |
|
557 |
|
558 fun assume_pred lit mk_lit cert r ({repr, rdata, apps, trace, prf, back}: context) = |
|
559 (case ritem_of_pred rdata r of |
|
560 {size, class, occs, neqs, atoms=Preds ts}: ritem => |
|
561 let val rdata = put_ritem r (mk_ritem size class occs neqs cert) rdata |
|
562 in without lit (map mk_lit ts, mk_context repr rdata apps trace prf back) end |
|
563 | _ => raise Fail "bad predicate assumption") |
|
564 |
|
565 (* |
|
566 Assumed equalities "t1 = t2" are treated as flat equalities between terms t1 and t2. |
|
567 If t1 and t2 are applications, congruences are propagated as part of the merge between t1 and t2. |
|
568 Negated equalities are handled likewise. |
|
569 |
|
570 Assumed predicates do not trigger congruences. Only predicates of the same class are implied. |
|
571 *) |
|
572 |
|
573 fun assume (lp as (Argo_Lit.Pos (Argo_Term.T (_, Argo_Expr.Eq, [t1, t2])), _)) cx = |
|
574 flat_merge lp (t1, t2) cx |
|
575 | assume (lp as (Argo_Lit.Neg (Argo_Term.T (_, Argo_Expr.Eq, [t1, t2])), _)) cx = |
|
576 flat_neq lp (t1, t2) cx |
|
577 | assume (lp as (lit as Argo_Lit.Pos (t as Argo_Term.T (_, Argo_Expr.App, [_, _])), _)) cx = |
|
578 assume_pred lit Argo_Lit.Pos (Cert lp) (repr_of' cx t) cx |
|
579 | assume (lp as (lit as Argo_Lit.Neg (t as Argo_Term.T (_, Argo_Expr.App, [_, _])), _)) cx = |
|
580 assume_pred lit Argo_Lit.Neg (Cert lp) (repr_of' cx t) cx |
|
581 | assume _ cx = (Argo_Common.Implied [], cx) |
|
582 |
|
583 |
|
584 (* checking for consistency and pending implications *) |
|
585 |
|
586 (* |
|
587 The internal model is always kept consistent. All implications are propagated as soon as |
|
588 new information is assumed. Hence, there is nothing to be done here. |
|
589 *) |
|
590 |
|
591 fun check cx = (Argo_Common.Implied [], cx) |
|
592 |
|
593 |
|
594 (* explanations *) |
|
595 |
|
596 (* |
|
597 The explanation for the predicate t, which is an application of t1 and t2, is constructed |
|
598 from the explanation of the predicate application "u1 u2" as well as the equalities "u1 = t1" |
|
599 and "u2 = t2" which both are constructed from the proof forest. The substitution rule is |
|
600 the proof step that concludes "t1 t2" from "u1 u2" and the two equalities "u1 = t1" |
|
601 and "u2 = t2". |
|
602 |
|
603 The atoms part of the ritem of t's representative must be a certificate of an already |
|
604 assumed predicate for otherwise there would be no explanation for t. |
|
605 *) |
|
606 |
|
607 fun explain_pred lit t t1 t2 ({repr, rdata, apps, trace, prf, back}: context) = |
|
608 (case ritem_of_pred rdata (repr_of repr t) of |
|
609 {atoms=Cert (cert as (lit', _)), ...}: ritem => |
|
610 let |
|
611 val (u1, u2) = dest_app (Argo_Lit.term_of lit') |
|
612 val (lits, (p, prf)) = proof_of cert [] prf |
|
613 val (lits, (p1, prf)) = mk_eq_proof trace u1 t1 lits prf |
|
614 val (lits, (p2, prf)) = mk_eq_proof trace u2 t2 lits prf |
|
615 val (lits, (p, prf)) = Argo_Proof.mk_subst p p1 p2 prf |> close_proof lit lits |
|
616 in ((lits, p), mk_context repr rdata apps trace prf back) end |
|
617 | _ => raise Fail "no explanation for bad predicate") |
|
618 |
|
619 (* |
|
620 Explanations are produced based on the proof forest that is constructed while assuming new |
|
621 information and propagating this among the internal data structures. |
|
622 |
|
623 For predicates, no distinction between both polarities needs to be done here. The atoms |
|
624 part of the relevant ritem knows the assumed polarity. |
|
625 *) |
|
626 |
|
627 fun explain (lit as Argo_Lit.Pos (Argo_Term.T (_, Argo_Expr.Eq, [t1, t2]))) cx = |
|
628 SOME (explain_eq lit t1 t2 cx) |
|
629 | explain (lit as Argo_Lit.Neg (Argo_Term.T (_, Argo_Expr.Eq, [t1, t2]))) cx = |
|
630 let val (_, eq) = the (find_neq cx (ritem_of' cx (repr_of' cx t1)) (repr_of' cx t2)) |
|
631 in SOME (explain_neq (Flat ((lit, NONE), (t1, t2))) eq cx) end |
|
632 | explain (lit as (Argo_Lit.Pos (t as Argo_Term.T (_, Argo_Expr.App, [t1, t2])))) cx = |
|
633 SOME (explain_pred lit t t1 t2 cx) |
|
634 | explain (lit as (Argo_Lit.Neg (t as Argo_Term.T (_, Argo_Expr.App, [t1, t2])))) cx = |
|
635 SOME (explain_pred lit t t1 t2 cx) |
|
636 | explain _ _ = NONE |
|
637 |
|
638 |
|
639 (* backtracking *) |
|
640 |
|
641 (* |
|
642 All information that needs to be reconstructed on backtracking is stored on the backtracking |
|
643 stack. On backtracking any current information is replaced by what was stored before. No copying |
|
644 nor subtle updates are required thanks to immutable data structures. |
|
645 *) |
|
646 |
|
647 fun add_level ({repr, rdata, apps, trace, prf, back}: context) = |
|
648 mk_context repr rdata apps trace prf ((repr, rdata, apps, trace) :: back) |
|
649 |
|
650 fun backtrack ({back=[], ...}: context) = raise Empty |
|
651 | backtrack ({prf, back=(repr, rdata, apps, trace) :: back, ...}: context) = |
|
652 mk_context repr rdata apps trace prf back |
|
653 |
|
654 end |