1 (* Title: HOL/Tools/SMT/z3_proof_reconstruction.ML
2 Author: Sascha Boehme, TU Muenchen
4 Proof reconstruction for proofs found by Z3.
7 signature Z3_PROOF_RECONSTRUCTION =
9 val add_z3_rule: thm -> Context.generic -> Context.generic
10 val reconstruct: Proof.context -> SMT_Translate.recon -> string list ->
12 val setup: theory -> theory
15 structure Z3_Proof_Reconstruction: Z3_PROOF_RECONSTRUCTION =
19 fun z3_exn msg = raise SMT_Failure.SMT (SMT_Failure.Other_Failure
20 ("Z3 proof reconstruction: " ^ msg))
24 (* net of schematic rules *)
26 val z3_ruleN = "z3_rule"
29 val description = "declaration of Z3 proof rules"
33 structure Z3_Rules = Generic_Data
38 val merge = Net.merge eq
42 `Thm.prop_of o Simplifier.rewrite_rule [Z3_Proof_Literals.rewrite_true]
44 fun ins thm net = Net.insert_term eq (prep thm) net handle Net.INSERT => net
45 fun del thm net = Net.delete_term eq (prep thm) net handle Net.DELETE => net
47 val add = Thm.declaration_attribute (Z3_Rules.map o ins)
48 val del = Thm.declaration_attribute (Z3_Rules.map o del)
51 val add_z3_rule = Z3_Rules.map o ins
53 fun by_schematic_rule ctxt ct =
54 the (Z3_Proof_Tools.net_instance (Z3_Rules.get (Context.Proof ctxt)) ct)
57 Attrib.setup (Binding.name z3_ruleN) (Attrib.add_del add del) description #>
58 Global_Theory.add_thms_dynamic (Binding.name z3_ruleN, Net.content o Z3_Rules.get)
66 fun named ctxt name prover ct =
67 let val _ = SMT_Config.trace_msg ctxt I ("Z3: trying " ^ name ^ " ...")
70 fun NAMED ctxt name tac i st =
71 let val _ = SMT_Config.trace_msg ctxt I ("Z3: trying " ^ name ^ " ...")
74 fun pretty_goal ctxt thms t =
75 [Pretty.block [Pretty.str "proposition: ", Syntax.pretty_term ctxt t]]
76 |> not (null thms) ? cons (Pretty.big_list "assumptions:"
77 (map (Display.pretty_thm ctxt) thms))
79 fun try_apply ctxt thms =
81 fun try_apply_err ct = Pretty.string_of (Pretty.chunks [
82 Pretty.big_list ("Z3 found a proof," ^
83 " but proof reconstruction failed at the following subgoal:")
84 (pretty_goal ctxt thms (Thm.term_of ct)),
85 Pretty.str ("Adding a rule to the lemma group " ^ quote z3_ruleN ^
86 " might solve this problem.")])
88 fun apply [] ct = error (try_apply_err ct)
89 | apply (prover :: provers) ct =
90 (case try prover ct of
91 SOME thm => (SMT_Config.trace_msg ctxt I "Z3: succeeded"; thm)
92 | NONE => apply provers ct)
94 fun schematic_label full = "schematic rules" |> full ? suffix " (full)"
95 fun schematic ctxt full ct =
97 |> full ? fold_rev (curry Drule.mk_implies o Thm.cprop_of) thms
98 |> named ctxt (schematic_label full) (by_schematic_rule ctxt)
99 |> fold Thm.elim_implies thms
101 in apply o cons (schematic ctxt false) o cons (schematic ctxt true) end
105 @{lemma "(if P then Q1 else Q2) = ((P --> Q1) & (~P --> Q2))" by simp}
108 fun HOL_fast_tac ctxt =
109 Classical.fast_tac (put_claset HOL_cs ctxt)
111 fun simp_fast_tac ctxt =
112 Simplifier.simp_tac (HOL_ss addsimps [rewr_if])
113 THEN_ALL_NEW HOL_fast_tac ctxt
119 (* theorems and proofs *)
121 (** theorem incarnations **)
124 Thm of thm | (* theorem without special features *)
125 MetaEq of thm | (* meta equality "t == s" *)
126 Literals of thm * Z3_Proof_Literals.littab
127 (* "P1 & ... & Pn" and table of all literals P1, ..., Pn *)
129 fun thm_of (Thm thm) = thm
130 | thm_of (MetaEq thm) = thm COMP @{thm meta_eq_to_obj_eq}
131 | thm_of (Literals (thm, _)) = thm
133 fun meta_eq_of (MetaEq thm) = thm
134 | meta_eq_of p = mk_meta_eq (thm_of p)
136 fun literals_of (Literals (_, lits)) = lits
137 | literals_of p = Z3_Proof_Literals.make_littab [thm_of p]
141 (** core proof rules **)
146 val remove_trigger = mk_meta_eq @{thm SMT.trigger_def}
147 val remove_weight = mk_meta_eq @{thm SMT.weight_def}
148 val remove_fun_app = mk_meta_eq @{thm SMT.fun_app_def}
150 fun rewrite_conv _ [] = Conv.all_conv
151 | rewrite_conv ctxt eqs = Simplifier.full_rewrite
152 (Simplifier.context ctxt Simplifier.empty_ss addsimps eqs)
154 val prep_rules = [@{thm Let_def}, remove_trigger, remove_weight,
155 remove_fun_app, Z3_Proof_Literals.rewrite_true]
158 | rewrite ctxt eqs = Conv.fconv_rule (rewrite_conv ctxt eqs)
160 fun burrow_snd_option f (i, thm) = Option.map (pair i) (f thm)
162 fun lookup_assm assms_net ct =
163 Z3_Proof_Tools.net_instance' burrow_snd_option assms_net ct
164 |> Option.map (fn ithm as (_, thm) => (ithm, Thm.cprop_of thm aconvc ct))
167 fun add_asserted outer_ctxt rewrite_rules assms asserted ctxt =
169 val eqs = map (rewrite ctxt [Z3_Proof_Literals.rewrite_true]) rewrite_rules
170 val eqs' = union Thm.eq_thm eqs prep_rules
174 |> map (apsnd (rewrite ctxt eqs'))
175 |> map (apsnd (Conv.fconv_rule Thm.eta_conversion))
176 |> Z3_Proof_Tools.thm_net_of snd
178 fun revert_conv ctxt = rewrite_conv ctxt eqs' then_conv Thm.eta_conversion
180 fun assume thm ctxt =
182 val ct = Thm.cprem_of thm 1
183 val (thm', ctxt') = yield_singleton Assumption.add_assumes ct ctxt
184 in (Thm.implies_elim thm thm', ctxt') end
186 fun add (idx, ct) ((is, thms), (ctxt, ptab)) =
190 |> Conv.fconv_rule (Conv.arg1_conv (revert_conv outer_ctxt))
191 val thm2 = singleton (Variable.export ctxt outer_ctxt) thm1
193 (case lookup_assm assms_net (Thm.cprem_of thm2 1) of
195 let val (thm, ctxt') = assume thm1 ctxt
196 in ((is, thms), (ctxt', Inttab.update (idx, Thm thm) ptab)) end
197 | SOME ((i, th), exact) =>
200 if exact then (Thm.implies_elim thm1 th, ctxt)
201 else assume thm1 ctxt
202 val thms' = if exact then thms else th :: thms
204 ((insert (op =) i is, thms'),
205 (ctxt', Inttab.update (idx, Thm thm) ptab))
208 in fold add asserted (([], []), (ctxt, Inttab.empty)) end
213 (* P = Q ==> P ==> Q or P --> Q ==> P ==> Q *)
215 val precomp = Z3_Proof_Tools.precompose2
216 val comp = Z3_Proof_Tools.compose
218 val meta_iffD1 = @{lemma "P == Q ==> P ==> (Q::bool)" by simp}
219 val meta_iffD1_c = precomp Thm.dest_binop meta_iffD1
221 val iffD1_c = precomp (Thm.dest_binop o Thm.dest_arg) @{thm iffD1}
222 val mp_c = precomp (Thm.dest_binop o Thm.dest_arg) @{thm mp}
224 fun mp (MetaEq thm) p = Thm (Thm.implies_elim (comp meta_iffD1_c thm) p)
228 val thm = comp iffD1_c pq handle THM _ => comp mp_c pq
229 in Thm (Thm.implies_elim thm p) end
233 (* and_elim: P1 & ... & Pn ==> Pi *)
234 (* not_or_elim: ~(P1 | ... | Pn) ==> ~Pi *)
236 fun is_sublit conj t = Z3_Proof_Literals.exists_lit conj (fn u => u aconv t)
238 fun derive conj t lits idx ptab =
240 val lit = the (Z3_Proof_Literals.get_first_lit (is_sublit conj t) lits)
241 val ls = Z3_Proof_Literals.explode conj false false [t] lit
242 val lits' = fold Z3_Proof_Literals.insert_lit ls
243 (Z3_Proof_Literals.delete_lit lit lits)
245 fun upd thm = Literals (thm_of thm, lits')
246 val ptab' = Inttab.map_entry idx upd ptab
247 in (the (Z3_Proof_Literals.lookup_lit lits' t), ptab') end
249 fun lit_elim conj (p, idx) ct ptab =
250 let val lits = literals_of p
252 (case Z3_Proof_Literals.lookup_lit lits (SMT_Utils.term_of ct) of
253 SOME lit => (Thm lit, ptab)
254 | NONE => apfst Thm (derive conj (SMT_Utils.term_of ct) lits idx ptab))
257 val and_elim = lit_elim true
258 val not_or_elim = lit_elim false
262 (* P1, ..., Pn |- False ==> |- ~P1 | ... | ~Pn *)
265 Thm.implies_elim (Thm.implies_intr (Thm.cprop_of lit) thm) lit
266 val explode_disj = Z3_Proof_Literals.explode false false false
267 fun intro hyps thm th = fold step (explode_disj hyps th) thm
269 fun dest_ccontr ct = [Thm.dest_arg (Thm.dest_arg (Thm.dest_arg1 ct))]
270 val ccontr = Z3_Proof_Tools.precompose dest_ccontr @{thm ccontr}
274 val cu = Z3_Proof_Literals.negate (Thm.dest_arg ct)
275 val hyps = map_filter (try HOLogic.dest_Trueprop) (Thm.hyps_of thm)
276 val th = Z3_Proof_Tools.under_assumption (intro hyps thm) cu
277 in Thm (Z3_Proof_Tools.compose ccontr th) end
281 (* \/{P1, ..., Pn, Q1, ..., Qn}, ~P1, ..., ~Pn ==> \/{Q1, ..., Qn} *)
283 val explode_disj = Z3_Proof_Literals.explode false true false
284 val join_disj = Z3_Proof_Literals.join false
285 fun unit thm thms th =
287 val t = @{const Not} $ SMT_Utils.prop_of thm
288 val ts = map SMT_Utils.prop_of thms
290 join_disj (Z3_Proof_Literals.make_littab (thms @ explode_disj ts th)) t
293 fun dest_arg2 ct = Thm.dest_arg (Thm.dest_arg ct)
294 fun dest ct = pairself dest_arg2 (Thm.dest_binop ct)
296 Z3_Proof_Tools.precompose2 dest @{lemma "(~P ==> ~Q) ==> Q ==> P" by fast}
298 fun unit_resolution thm thms ct =
299 Z3_Proof_Literals.negate (Thm.dest_arg ct)
300 |> Z3_Proof_Tools.under_assumption (unit thm thms)
301 |> Thm o Z3_Proof_Tools.discharge thm o Z3_Proof_Tools.compose contrapos
305 (* P ==> P == True or P ==> P == False *)
307 val iff1 = @{lemma "P ==> P == (~ False)" by simp}
308 val iff2 = @{lemma "~P ==> P == False" by simp}
310 fun iff_true thm = MetaEq (thm COMP iff1)
311 fun iff_false thm = MetaEq (thm COMP iff2)
315 (* distributivity of | over & *)
316 fun distributivity ctxt = Thm o try_apply ctxt [] [
317 named ctxt "fast" (Z3_Proof_Tools.by_tac (HOL_fast_tac ctxt))]
318 (* FIXME: not very well tested *)
321 (* Tseitin-like axioms *)
323 val disjI1 = @{lemma "(P ==> Q) ==> ~P | Q" by fast}
324 val disjI2 = @{lemma "(~P ==> Q) ==> P | Q" by fast}
325 val disjI3 = @{lemma "(~Q ==> P) ==> P | Q" by fast}
326 val disjI4 = @{lemma "(Q ==> P) ==> P | ~Q" by fast}
328 fun prove' conj1 conj2 ct2 thm =
331 Z3_Proof_Literals.explode conj1 true (conj1 <> conj2) [] thm
332 |> cons Z3_Proof_Literals.true_thm
333 |> Z3_Proof_Literals.make_littab
334 in Z3_Proof_Literals.join conj2 littab (Thm.term_of ct2) end
336 fun prove rule (ct1, conj1) (ct2, conj2) =
337 Z3_Proof_Tools.under_assumption (prove' conj1 conj2 ct2) ct1 COMP rule
339 fun prove_def_axiom ct =
340 let val (ct1, ct2) = Thm.dest_binop (Thm.dest_arg ct)
342 (case Thm.term_of ct1 of
343 @{const Not} $ (@{const HOL.conj} $ _ $ _) =>
344 prove disjI1 (Thm.dest_arg ct1, true) (ct2, true)
345 | @{const HOL.conj} $ _ $ _ =>
346 prove disjI3 (Z3_Proof_Literals.negate ct2, false) (ct1, true)
347 | @{const Not} $ (@{const HOL.disj} $ _ $ _) =>
348 prove disjI3 (Z3_Proof_Literals.negate ct2, false) (ct1, false)
349 | @{const HOL.disj} $ _ $ _ =>
350 prove disjI2 (Z3_Proof_Literals.negate ct1, false) (ct2, true)
351 | Const (@{const_name distinct}, _) $ _ =>
353 fun dis_conv cv = Conv.arg_conv (Conv.arg1_conv cv)
354 val unfold_dis_conv = dis_conv Z3_Proof_Tools.unfold_distinct_conv
356 let val (cu1, cu2) = Thm.dest_binop (Thm.dest_arg cu)
357 in prove disjI4 (Thm.dest_arg cu2, true) (cu1, true) end
358 in Z3_Proof_Tools.with_conv unfold_dis_conv prv ct end
359 | @{const Not} $ (Const (@{const_name distinct}, _) $ _) =>
361 fun dis_conv cv = Conv.arg_conv (Conv.arg1_conv (Conv.arg_conv cv))
362 val unfold_dis_conv = dis_conv Z3_Proof_Tools.unfold_distinct_conv
364 let val (cu1, cu2) = Thm.dest_binop (Thm.dest_arg cu)
365 in prove disjI1 (Thm.dest_arg cu1, true) (cu2, true) end
366 in Z3_Proof_Tools.with_conv unfold_dis_conv prv ct end
367 | _ => raise CTERM ("prove_def_axiom", [ct]))
370 fun def_axiom ctxt = Thm o try_apply ctxt [] [
371 named ctxt "conj/disj/distinct" prove_def_axiom,
372 Z3_Proof_Tools.by_abstraction 0 (true, false) ctxt [] (fn ctxt' =>
373 named ctxt' "simp+fast" (Z3_Proof_Tools.by_tac (simp_fast_tac ctxt')))]
377 (* local definitions *)
380 @{lemma "n == P ==> (~n | P) & (n | ~P)" by simp},
381 @{lemma "n == (if P then s else t) ==> (~P | n = s) & (P | n = t)"
383 @{lemma "n == P ==> n = P" by (rule meta_eq_to_obj_eq)} ]
386 @{lemma "(~n | P) & (n | ~P) ==> P == n" by (atomize(full)) fast},
387 @{lemma "(~P | n = s) & (P | n = t) ==> (if P then s else t) == n"
388 by (atomize(full)) fastforce} ]
390 val inst_rule = Z3_Proof_Tools.match_instantiate Thm.dest_arg
393 (case get_first (try (inst_rule ct)) intro_rules of
395 | NONE => raise CTERM ("intro_def", [ct]))
397 fun intro_def ct = Z3_Proof_Tools.make_hyp_def (apply_rule ct) #>> Thm
400 get_first (try (fn rule => MetaEq (thm COMP rule))) apply_rules
401 |> the_default (Thm thm)
405 (* negation normal form *)
407 val quant_rules1 = ([
408 @{lemma "(!!x. P x == Q) ==> ALL x. P x == Q" by simp},
409 @{lemma "(!!x. P x == Q) ==> EX x. P x == Q" by simp}], [
410 @{lemma "(!!x. P x == Q x) ==> ALL x. P x == ALL x. Q x" by simp},
411 @{lemma "(!!x. P x == Q x) ==> EX x. P x == EX x. Q x" by simp}])
413 val quant_rules2 = ([
414 @{lemma "(!!x. ~P x == Q) ==> ~(ALL x. P x) == Q" by simp},
415 @{lemma "(!!x. ~P x == Q) ==> ~(EX x. P x) == Q" by simp}], [
416 @{lemma "(!!x. ~P x == Q x) ==> ~(ALL x. P x) == EX x. Q x" by simp},
417 @{lemma "(!!x. ~P x == Q x) ==> ~(EX x. P x) == ALL x. Q x" by simp}])
419 fun nnf_quant_tac thm (qs as (qs1, qs2)) i st = (
420 Tactic.rtac thm ORELSE'
421 (Tactic.match_tac qs1 THEN' nnf_quant_tac thm qs) ORELSE'
422 (Tactic.match_tac qs2 THEN' nnf_quant_tac thm qs)) i st
424 fun nnf_quant_tac_varified vars eq =
425 nnf_quant_tac (Z3_Proof_Tools.varify vars eq)
427 fun nnf_quant vars qs p ct =
428 Z3_Proof_Tools.as_meta_eq ct
429 |> Z3_Proof_Tools.by_tac (nnf_quant_tac_varified vars (meta_eq_of p) qs)
431 fun prove_nnf ctxt = try_apply ctxt [] [
432 named ctxt "conj/disj" Z3_Proof_Literals.prove_conj_disj_eq,
433 Z3_Proof_Tools.by_abstraction 0 (true, false) ctxt [] (fn ctxt' =>
434 named ctxt' "simp+fast" (Z3_Proof_Tools.by_tac (simp_fast_tac ctxt')))]
436 fun nnf ctxt vars ps ct =
437 (case SMT_Utils.term_of ct of
438 _ $ (l as Const _ $ Abs _) $ (r as Const _ $ Abs _) =>
440 then MetaEq (Thm.reflexive (Thm.dest_arg (Thm.dest_arg ct)))
441 else MetaEq (nnf_quant vars quant_rules1 (hd ps) ct)
442 | _ $ (@{const Not} $ (Const _ $ Abs _)) $ (Const _ $ Abs _) =>
443 MetaEq (nnf_quant vars quant_rules2 (hd ps) ct)
446 val nnf_rewr_conv = Conv.arg_conv (Conv.arg_conv
447 (Z3_Proof_Tools.unfold_eqs ctxt
448 (map (Thm.symmetric o meta_eq_of) ps)))
449 in Thm (Z3_Proof_Tools.with_conv nnf_rewr_conv (prove_nnf ctxt) ct) end)
454 (** equality proof rules **)
457 fun refl ct = MetaEq (Thm.reflexive (Thm.dest_arg (Thm.dest_arg ct)))
460 (* s = t ==> t = s *)
462 val symm_rule = @{lemma "s = t ==> t == s" by simp}
464 fun symm (MetaEq thm) = MetaEq (Thm.symmetric thm)
465 | symm p = MetaEq (thm_of p COMP symm_rule)
469 (* s = t ==> t = u ==> s = u *)
471 val trans1 = @{lemma "s == t ==> t = u ==> s == u" by simp}
472 val trans2 = @{lemma "s = t ==> t == u ==> s == u" by simp}
473 val trans3 = @{lemma "s = t ==> t = u ==> s == u" by simp}
475 fun trans (MetaEq thm1) (MetaEq thm2) = MetaEq (Thm.transitive thm1 thm2)
476 | trans (MetaEq thm) q = MetaEq (thm_of q COMP (thm COMP trans1))
477 | trans p (MetaEq thm) = MetaEq (thm COMP (thm_of p COMP trans2))
478 | trans p q = MetaEq (thm_of q COMP (thm_of p COMP trans3))
482 (* t1 = s1 ==> ... ==> tn = sn ==> f t1 ... tn = f s1 .. sn
483 (reflexive antecendents are droppped) *)
487 fun prove_refl (ct, _) = Thm.reflexive ct
488 fun prove_comb f g cp =
489 let val ((ct1, ct2), (cu1, cu2)) = pairself Thm.dest_comb cp
490 in Thm.combination (f (ct1, cu1)) (g (ct2, cu2)) end
491 fun prove_arg f = prove_comb prove_refl f
493 fun prove f cp = prove_comb (prove f) f cp handle CTERM _ => prove_refl cp
495 fun prove_nary is_comb f =
497 fun prove (cp as (ct, _)) = f cp handle MONO =>
498 if is_comb (Thm.term_of ct)
499 then prove_comb (prove_arg prove) prove cp
503 fun prove_list f n cp =
504 if n = 0 then prove_refl cp
505 else prove_comb (prove_arg f) (prove_list f (n-1)) cp
507 fun with_length f (cp as (cl, _)) =
508 f (length (HOLogic.dest_list (Thm.term_of cl))) cp
510 fun prove_distinct f = prove_arg (with_length (prove_list f))
512 fun prove_eq exn lookup cp =
513 (case lookup (Logic.mk_equals (pairself Thm.term_of cp)) of
515 | NONE => if exn then raise MONO else prove_refl cp)
517 val prove_exn = prove_eq true
518 and prove_safe = prove_eq false
520 fun mono f (cp as (cl, _)) =
521 (case Term.head_of (Thm.term_of cl) of
522 @{const HOL.conj} => prove_nary Z3_Proof_Literals.is_conj (prove_exn f)
523 | @{const HOL.disj} => prove_nary Z3_Proof_Literals.is_disj (prove_exn f)
524 | Const (@{const_name distinct}, _) => prove_distinct (prove_safe f)
525 | _ => prove (prove_safe f)) cp
527 fun monotonicity eqs ct =
529 fun and_symmetric (t, thm) = [(t, thm), (t, Thm.symmetric thm)]
530 val teqs = maps (and_symmetric o `Thm.prop_of o meta_eq_of) eqs
531 val lookup = AList.lookup (op aconv) teqs
532 val cp = Thm.dest_binop (Thm.dest_arg ct)
533 in MetaEq (prove_exn lookup cp handle MONO => mono lookup cp) end
537 (* |- f a b = f b a (where f is equality) *)
539 val rule = @{lemma "a = b == b = a" by (atomize(full)) (rule eq_commute)}
541 fun commutativity ct =
542 MetaEq (Z3_Proof_Tools.match_instantiate I
543 (Z3_Proof_Tools.as_meta_eq ct) rule)
548 (** quantifier proof rules **)
550 (* P ?x = Q ?x ==> (ALL x. P x) = (ALL x. Q x)
551 P ?x = Q ?x ==> (EX x. P x) = (EX x. Q x) *)
554 @{lemma "(!!x. P x == Q x) ==> (ALL x. P x) == (ALL x. Q x)" by simp},
555 @{lemma "(!!x. P x == Q x) ==> (EX x. P x) == (EX x. Q x)" by simp}]
557 fun quant_intro vars p ct =
559 val thm = meta_eq_of p
560 val rules' = Z3_Proof_Tools.varify vars thm :: rules
561 val cu = Z3_Proof_Tools.as_meta_eq ct
562 val tac = REPEAT_ALL_NEW (Tactic.match_tac rules')
563 in MetaEq (Z3_Proof_Tools.by_tac tac cu) end
567 (* |- ((ALL x. P x) | Q) = (ALL x. P x | Q) *)
568 fun pull_quant ctxt = Thm o try_apply ctxt [] [
569 named ctxt "fast" (Z3_Proof_Tools.by_tac (HOL_fast_tac ctxt))]
570 (* FIXME: not very well tested *)
573 (* |- (ALL x. P x & Q x) = ((ALL x. P x) & (ALL x. Q x)) *)
574 fun push_quant ctxt = Thm o try_apply ctxt [] [
575 named ctxt "fast" (Z3_Proof_Tools.by_tac (HOL_fast_tac ctxt))]
576 (* FIXME: not very well tested *)
579 (* |- (ALL x1 ... xn y1 ... yn. P x1 ... xn) = (ALL x1 ... xn. P x1 ... xn) *)
581 val elim_all = @{lemma "P = Q ==> (ALL x. P) = Q" by fast}
582 val elim_ex = @{lemma "P = Q ==> (EX x. P) = Q" by fast}
584 fun elim_unused_tac i st = (
585 Tactic.match_tac [@{thm refl}]
586 ORELSE' (Tactic.match_tac [elim_all, elim_ex] THEN' elim_unused_tac)
588 Tactic.match_tac [@{thm iff_allI}, @{thm iff_exI}]
589 THEN' elim_unused_tac)) i st
592 val elim_unused_vars = Thm o Z3_Proof_Tools.by_tac elim_unused_tac
597 (* |- (ALL x1 ... xn. ~(x1 = t1 & ... xn = tn) | P x1 ... xn) = P t1 ... tn *)
598 fun dest_eq_res ctxt = Thm o try_apply ctxt [] [
599 named ctxt "fast" (Z3_Proof_Tools.by_tac (HOL_fast_tac ctxt))]
600 (* FIXME: not very well tested *)
603 (* |- ~(ALL x1...xn. P x1...xn) | P a1...an *)
605 val rule = @{lemma "~ P x | Q ==> ~(ALL x. P x) | Q" by fast}
607 val quant_inst = Thm o Z3_Proof_Tools.by_tac (
608 REPEAT_ALL_NEW (Tactic.match_tac [rule])
609 THEN' Tactic.rtac @{thm excluded_middle})
613 (* |- (EX x. P x) = P c |- ~(ALL x. P x) = ~ P c *)
616 SMT_Utils.mk_const_pat @{theory} @{const_name all}
617 (SMT_Utils.destT1 o SMT_Utils.destT1)
618 fun mk_forall cv ct =
619 Thm.capply (SMT_Utils.instT' cv forall) (Thm.cabs cv ct)
621 fun get_vars f mk pred ctxt t =
622 Term.fold_aterms f t []
623 |> map_filter (fn v =>
624 if pred v then SOME (SMT_Utils.certify ctxt (mk v)) else NONE)
626 fun close vars f ct ctxt =
628 val frees_of = get_vars Term.add_frees Free (member (op =) vars o fst)
629 val vs = frees_of ctxt (Thm.term_of ct)
630 val (thm, ctxt') = f (fold_rev mk_forall vs ct) ctxt
631 val vars_of = get_vars Term.add_vars Var (K true) ctxt'
632 in (Thm.instantiate ([], vars_of (Thm.prop_of thm) ~~ vs) thm, ctxt') end
634 val sk_rules = @{lemma
635 "c = (SOME x. P x) ==> (EX x. P x) = P c"
636 "c = (SOME x. ~P x) ==> (~(ALL x. P x)) = (~P c)"
637 by (metis someI_ex)+}
641 apfst Thm oo close vars (yield_singleton Assumption.add_assumes)
643 fun discharge_sk_tac i st = (
644 Tactic.rtac @{thm trans} i
645 THEN Tactic.resolve_tac sk_rules i
646 THEN (Tactic.rtac @{thm refl} ORELSE' discharge_sk_tac) (i+1)
647 THEN Tactic.rtac @{thm refl} i) st
653 (** theory proof rules **)
655 (* theory lemmas: linear arithmetic, arrays *)
656 fun th_lemma ctxt simpset thms = Thm o try_apply ctxt thms [
657 Z3_Proof_Tools.by_abstraction 0 (false, true) ctxt thms (fn ctxt' =>
658 Z3_Proof_Tools.by_tac (
659 NAMED ctxt' "arith" (Arith_Data.arith_tac ctxt')
660 ORELSE' NAMED ctxt' "simp+arith" (
661 Simplifier.asm_full_simp_tac simpset
662 THEN_ALL_NEW Arith_Data.arith_tac ctxt')))]
665 (* rewriting: prove equalities:
666 * ACI of conjunction/disjunction
667 * contradiction, excluded middle
668 * logical rewriting rules (for negation, implication, equivalence,
670 * normal forms for polynoms (integer/real arithmetic)
671 * quantifier elimination over linear arithmetic
673 structure Z3_Simps = Named_Thms
676 val description = "simplification rules for Z3 proof reconstruction"
680 fun spec_meta_eq_of thm =
681 (case try (fn th => th RS @{thm spec}) thm of
682 SOME thm' => spec_meta_eq_of thm'
683 | NONE => mk_meta_eq thm)
685 fun prep (Thm thm) = spec_meta_eq_of thm
686 | prep (MetaEq thm) = thm
687 | prep (Literals (thm, _)) = spec_meta_eq_of thm
689 fun unfold_conv ctxt ths =
690 Conv.arg_conv (Conv.binop_conv (Z3_Proof_Tools.unfold_eqs ctxt
693 fun with_conv _ [] prv = prv
694 | with_conv ctxt ths prv =
695 Z3_Proof_Tools.with_conv (unfold_conv ctxt ths) prv
698 Conv.arg_conv (Conv.binop_conv
699 (Conv.try_conv Z3_Proof_Tools.unfold_distinct_conv))
700 val prove_conj_disj_eq =
701 Z3_Proof_Tools.with_conv unfold_conv Z3_Proof_Literals.prove_conj_disj_eq
703 fun declare_hyps ctxt thm =
704 (thm, snd (Assumption.add_assumes (#hyps (Thm.crep_thm thm)) ctxt))
707 val abstraction_depth = 3
709 This value was chosen large enough to potentially catch exceptions,
710 yet small enough to not cause too much harm. The value might be
711 increased in the future, if reconstructing 'rewrite' fails on problems
712 that get too much abstracted to be reconstructable.
715 fun rewrite simpset ths ct ctxt =
716 apfst Thm (declare_hyps ctxt (with_conv ctxt ths (try_apply ctxt [] [
717 named ctxt "conj/disj/distinct" prove_conj_disj_eq,
718 Z3_Proof_Tools.by_abstraction 0 (true, false) ctxt [] (fn ctxt' =>
719 Z3_Proof_Tools.by_tac (
720 NAMED ctxt' "simp (logic)" (Simplifier.simp_tac simpset)
721 THEN_ALL_NEW NAMED ctxt' "fast (logic)" (fast_tac ctxt'))),
722 Z3_Proof_Tools.by_abstraction 0 (false, true) ctxt [] (fn ctxt' =>
723 Z3_Proof_Tools.by_tac (
724 (Tactic.rtac @{thm iff_allI} ORELSE' K Tactical.all_tac)
725 THEN' NAMED ctxt' "simp (theory)" (Simplifier.simp_tac simpset)
727 NAMED ctxt' "fast (theory)" (HOL_fast_tac ctxt')
728 ORELSE' NAMED ctxt' "arith (theory)" (Arith_Data.arith_tac ctxt')))),
729 Z3_Proof_Tools.by_abstraction 0 (true, true) ctxt [] (fn ctxt' =>
730 Z3_Proof_Tools.by_tac (
731 (Tactic.rtac @{thm iff_allI} ORELSE' K Tactical.all_tac)
732 THEN' NAMED ctxt' "simp (full)" (Simplifier.simp_tac simpset)
734 NAMED ctxt' "fast (full)" (HOL_fast_tac ctxt')
735 ORELSE' NAMED ctxt' "arith (full)" (Arith_Data.arith_tac ctxt')))),
736 named ctxt "injectivity" (Z3_Proof_Methods.prove_injectivity ctxt),
737 Z3_Proof_Tools.by_abstraction abstraction_depth (true, true) ctxt []
739 Z3_Proof_Tools.by_tac (
740 (Tactic.rtac @{thm iff_allI} ORELSE' K Tactical.all_tac)
741 THEN' NAMED ctxt' "simp (deepen)" (Simplifier.simp_tac simpset)
743 NAMED ctxt' "fast (deepen)" (HOL_fast_tac ctxt')
744 ORELSE' NAMED ctxt' "arith (deepen)" (Arith_Data.arith_tac
751 (* proof reconstruction *)
753 (** tracing and checking **)
755 fun trace_before ctxt idx = SMT_Config.trace_msg ctxt (fn r =>
756 "Z3: #" ^ string_of_int idx ^ ": " ^ Z3_Proof_Parser.string_of_rule r)
758 fun check_after idx r ps ct (p, (ctxt, _)) =
759 if not (Config.get ctxt SMT_Config.trace) then ()
761 let val thm = thm_of p |> tap (Thm.join_proofs o single)
763 if (Thm.cprop_of thm) aconvc ct then ()
765 z3_exn (Pretty.string_of (Pretty.big_list
766 ("proof step failed: " ^ quote (Z3_Proof_Parser.string_of_rule r) ^
767 " (#" ^ string_of_int idx ^ ")")
768 (pretty_goal ctxt (map (thm_of o fst) ps) (Thm.prop_of thm) @
769 [Pretty.block [Pretty.str "expected: ",
770 Syntax.pretty_term ctxt (Thm.term_of ct)]])))
774 (** overall reconstruction procedure **)
777 fun not_supported r = raise Fail ("Z3: proof rule not implemented: " ^
778 quote (Z3_Proof_Parser.string_of_rule r))
780 fun prove_step simpset vars r ps ct (cxp as (cx, ptab)) =
783 (Z3_Proof_Parser.True_Axiom, _) => (Thm Z3_Proof_Literals.true_thm, cxp)
784 | (Z3_Proof_Parser.Asserted, _) => raise Fail "bad assertion"
785 | (Z3_Proof_Parser.Goal, _) => raise Fail "bad assertion"
786 | (Z3_Proof_Parser.Modus_Ponens, [(p, _), (q, _)]) =>
787 (mp q (thm_of p), cxp)
788 | (Z3_Proof_Parser.Modus_Ponens_Oeq, [(p, _), (q, _)]) =>
789 (mp q (thm_of p), cxp)
790 | (Z3_Proof_Parser.And_Elim, [(p, i)]) =>
791 and_elim (p, i) ct ptab ||> pair cx
792 | (Z3_Proof_Parser.Not_Or_Elim, [(p, i)]) =>
793 not_or_elim (p, i) ct ptab ||> pair cx
794 | (Z3_Proof_Parser.Hypothesis, _) => (Thm (Thm.assume ct), cxp)
795 | (Z3_Proof_Parser.Lemma, [(p, _)]) => (lemma (thm_of p) ct, cxp)
796 | (Z3_Proof_Parser.Unit_Resolution, (p, _) :: ps) =>
797 (unit_resolution (thm_of p) (map (thm_of o fst) ps) ct, cxp)
798 | (Z3_Proof_Parser.Iff_True, [(p, _)]) => (iff_true (thm_of p), cxp)
799 | (Z3_Proof_Parser.Iff_False, [(p, _)]) => (iff_false (thm_of p), cxp)
800 | (Z3_Proof_Parser.Distributivity, _) => (distributivity cx ct, cxp)
801 | (Z3_Proof_Parser.Def_Axiom, _) => (def_axiom cx ct, cxp)
802 | (Z3_Proof_Parser.Intro_Def, _) => intro_def ct cx ||> rpair ptab
803 | (Z3_Proof_Parser.Apply_Def, [(p, _)]) => (apply_def (thm_of p), cxp)
804 | (Z3_Proof_Parser.Iff_Oeq, [(p, _)]) => (p, cxp)
805 | (Z3_Proof_Parser.Nnf_Pos, _) => (nnf cx vars (map fst ps) ct, cxp)
806 | (Z3_Proof_Parser.Nnf_Neg, _) => (nnf cx vars (map fst ps) ct, cxp)
809 | (Z3_Proof_Parser.Reflexivity, _) => (refl ct, cxp)
810 | (Z3_Proof_Parser.Symmetry, [(p, _)]) => (symm p, cxp)
811 | (Z3_Proof_Parser.Transitivity, [(p, _), (q, _)]) => (trans p q, cxp)
812 | (Z3_Proof_Parser.Monotonicity, _) => (monotonicity (map fst ps) ct, cxp)
813 | (Z3_Proof_Parser.Commutativity, _) => (commutativity ct, cxp)
815 (* quantifier rules *)
816 | (Z3_Proof_Parser.Quant_Intro, [(p, _)]) => (quant_intro vars p ct, cxp)
817 | (Z3_Proof_Parser.Pull_Quant, _) => (pull_quant cx ct, cxp)
818 | (Z3_Proof_Parser.Push_Quant, _) => (push_quant cx ct, cxp)
819 | (Z3_Proof_Parser.Elim_Unused_Vars, _) => (elim_unused_vars ct, cxp)
820 | (Z3_Proof_Parser.Dest_Eq_Res, _) => (dest_eq_res cx ct, cxp)
821 | (Z3_Proof_Parser.Quant_Inst, _) => (quant_inst ct, cxp)
822 | (Z3_Proof_Parser.Skolemize, _) => skolemize vars ct cx ||> rpair ptab
825 | (Z3_Proof_Parser.Th_Lemma _, _) => (* FIXME: use arguments *)
826 (th_lemma cx simpset (map (thm_of o fst) ps) ct, cxp)
827 | (Z3_Proof_Parser.Rewrite, _) => rewrite simpset [] ct cx ||> rpair ptab
828 | (Z3_Proof_Parser.Rewrite_Star, ps) =>
829 rewrite simpset (map fst ps) ct cx ||> rpair ptab
831 | (Z3_Proof_Parser.Nnf_Star, _) => not_supported r
832 | (Z3_Proof_Parser.Cnf_Star, _) => not_supported r
833 | (Z3_Proof_Parser.Transitivity_Star, _) => not_supported r
834 | (Z3_Proof_Parser.Pull_Quant_Star, _) => not_supported r
836 | _ => raise Fail ("Z3: proof rule " ^
837 quote (Z3_Proof_Parser.string_of_rule r) ^
838 " has an unexpected number of arguments."))
840 fun lookup_proof ptab idx =
841 (case Inttab.lookup ptab idx of
843 | NONE => z3_exn ("unknown proof id: " ^ quote (string_of_int idx)))
845 fun prove simpset vars (idx, step) (_, cxp as (ctxt, ptab)) =
847 val Z3_Proof_Parser.Proof_Step {rule=r, prems, prop, ...} = step
848 val ps = map (lookup_proof ptab) prems
849 val _ = trace_before ctxt idx r
850 val (thm, (ctxt', ptab')) =
852 |> prove_step simpset vars r ps prop
853 |> tap (check_after idx r ps prop)
854 in (thm, (ctxt', Inttab.update (idx, thm) ptab')) end
856 fun make_discharge_rules rules = rules @ [@{thm allI}, @{thm refl},
857 @{thm reflexive}, Z3_Proof_Literals.true_thm]
859 fun discharge_tac rules =
860 Tactic.resolve_tac rules ORELSE' SOLVED' discharge_sk_tac
862 fun discharge_assms rules thm =
863 if Thm.nprems_of thm = 0 then Goal.norm_result thm
865 (case Seq.pull (discharge_tac rules 1 thm) of
866 SOME (thm', _) => discharge_assms rules thm'
867 | NONE => raise THM ("failed to discharge premise", 1, [thm]))
869 fun discharge rules outer_ctxt (p, (inner_ctxt, _)) =
871 |> singleton (Proof_Context.export inner_ctxt outer_ctxt)
872 |> discharge_assms (make_discharge_rules rules)
875 fun reconstruct outer_ctxt recon output =
877 val {context=ctxt, typs, terms, rewrite_rules, assms} = recon
878 val (asserted, steps, vars, ctxt1) =
879 Z3_Proof_Parser.parse ctxt typs terms output
881 val simpset = Z3_Proof_Tools.make_simpset ctxt1 (Z3_Simps.get ctxt1)
883 val ((is, rules), cxp as (ctxt2, _)) =
884 add_asserted outer_ctxt rewrite_rules assms asserted ctxt1
886 if Config.get ctxt2 SMT_Config.filter_only_facts then (is, @{thm TrueI})
888 (Thm @{thm TrueI}, cxp)
889 |> fold (prove simpset vars) steps
890 |> discharge rules outer_ctxt
896 val setup = z3_rules_setup #> Z3_Simps.setup