-
Notifications
You must be signed in to change notification settings - Fork 108
/
Corres_Cases.thy
323 lines (252 loc) · 14.8 KB
/
Corres_Cases.thy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
(*
* Copyright 2023, Proofcraft Pty Ltd
*
* SPDX-License-Identifier: BSD-2-Clause
*)
theory Corres_Cases
imports Corres_UL
begin
text \<open>
This file defines the following main methods for safe data type case distinctions on
corres/corres_underlying predicates.
\<^item> corres_cases_left: case distinction on abstract monad
\<^item> corres_cases_right: case distinction on concrete monad
\<^item> corres_cases: try corres_cases_left, then corres_cases_right
\<^item> corres_cases_both: simultaneous (quadratic) case distinction on both sides, with safe
elimination of trivially contradictory cases.
The first 3 methods take no arguments, corres_cases_both takes an optional simp argument to,
for example, unfold relations that synchronise cases between the abstract and concrete side.
The case distinctions work if the entire monad is a "case" statement, or if the monad is a
@{const bind} or @{const bindE} term with a "case" statement in the head position.
There is an existing method for case distinctions (@{method wpc}), but this method is not
flexible enough for @{term corres}: consider the goal
@{text "\<And>x. corres r (?G x) ?G' (case x of None \<Rightarrow> a | Some y \<Rightarrow> b y) m"} -- if we perform
case distinction on @{term x}, then we can transform @{text "?G x"} into
@{text "\<lambda>x s. (x = None \<longrightarrow> ?Q1 x s) \<and> (\<forall>y. x = Some y \<longrightarrow> ?Q2 x y s)"},
but we cannot do the same with @{text "?G'"}, because @{text "?G'"} does not depend on @{text x}.
The best we can do is @{text "?G' = \<lambda>s. ?A s \<and> ?B s"}, which so far seems to be good enough
in our manual proofs.
The @{method wpc} method will try to treat both preconditions uniformly and fail on @{text "?G'"}.
Extending @{method wpc} to deal with guards in a non-uniform way would be possible, but would make
setup for new constants even more messy than it already is. Instead we re-use the general idea
here (in Eisbach instead of ML), and leave the @{method wpc} setup clean for other uses.
\<close>
section \<open>Helper functions and definitions\<close>
(* The following three definitions are originally by Dan Matichuck from the Eisbach
CorresK_Method example *)
(* Retrieve a split rule for a target term that is expected to be a case statement. *)
ML \<open>
fun get_split_rule ctxt target =
let
val (hdTarget, args) = strip_comb (Envir.eta_contract target)
val (constNm, _) = dest_Const hdTarget
val constNm_fds = String.fields (fn c => c = #".") constNm
val _ = if String.isPrefix "case_" (List.last constNm_fds) then ()
else raise TERM ("Not a case statement", [target])
val typeNm = (String.concatWith "." o rev o tl o rev) constNm_fds
val split = Proof_Context.get_thm ctxt (typeNm ^ ".split")
val vars = Term.add_vars (Thm.prop_of split) []
val datatype_name = List.nth (rev constNm_fds, 1)
fun T_is_datatype (Type (nm, _)) = (Long_Name.base_name nm = Long_Name.base_name datatype_name)
| T_is_datatype _ = false
val datatype_var =
case find_first (fn ((_, _), T') => T_is_datatype T') vars of
SOME (ix, _) => ix
| NONE => error ("Couldn't find datatype in thm: " ^ datatype_name)
val split' = Drule.infer_instantiate ctxt
[(datatype_var, Thm.cterm_of ctxt (List.last args))] split
in SOME split' end
handle TERM _ => NONE;
\<close>
(* The above function as an attribute. The term argument is expected to be a case statement. *)
attribute_setup get_split_rule = \<open>Args.term >>
(fn t => Thm.rule_attribute [] (fn context => fn _ =>
case get_split_rule (Context.proof_of context) t of
SOME thm => thm
| NONE => Drule.free_dummy_thm))\<close>
(* Apply a split rule to a goal. Example usage:
apply_split f "\<lambda>f. corres_underlying sr nf nf' r P P' f f'"
The first (free) f is expected to be a case statement and is used to extract the split rule.
The second term is expected to take this f as a parameter and provide the term context of the
case statement in the goal so the split rule is applied to the correct occurrence of the case
statement.
*)
method apply_split for f :: 'a and R :: "'a \<Rightarrow> bool" =
(match [[get_split_rule f]] in U: "(?x :: bool) = ?y" \<Rightarrow>
\<open>match U[THEN iffD2] in U': "\<And>H. ?A \<Longrightarrow> H (?z :: 'c)" \<Rightarrow>
\<open>match (R) in "R' :: 'c \<Rightarrow> bool" for R' \<Rightarrow>
\<open>rule U'[where H=R']\<close>\<close>\<close>)
context
begin
(* This predicate provides an abstraction for guard/precondition terms for transformations
on those guards.
P and P' are the abstract and concrete preconditions before the transformation
Q and Q' are the abstract and concrete preconditions after the transformation
R is the predicate to be transformed.
*)
private definition corres_case_helper ::
"(('a \<Rightarrow> bool) \<times> ('b \<Rightarrow> bool)) \<Rightarrow> (('a \<Rightarrow> bool) \<times> ('b \<Rightarrow> bool)) \<Rightarrow> bool \<Rightarrow> bool" where
"corres_case_helper \<equiv> \<lambda>(P, P') (Q, Q') R. (\<forall>s. P s \<longrightarrow> Q s) \<longrightarrow> (\<forall>s. P' s \<longrightarrow> Q' s) \<longrightarrow> R"
(* The following lemmas enable us to lift preconditions of corres_case_helper over conjunction,
universal quantifiers, and implication. Note that there are strong versions for forall/implies
where both guards are treated uniformly, and weak versions, where forall/implies is dropped
in one guard, but not the other.
The collection of the lemmas below is used to process the term R in corres_case_helper and
create appropriately lifted guard/preconditions during that procedure. The names and general
idea are from the WPC theory.
*)
private lemma corres_case_helperI:
"corres_case_helper (P, P') (P, P') R \<Longrightarrow> R"
by (simp add: corres_case_helper_def)
private lemma corres_case_conj_process:
"\<lbrakk> corres_case_helper (P, P') (A, A') R; corres_case_helper (P, P') (B, B') S \<rbrakk>
\<Longrightarrow> corres_case_helper (P, P') (\<lambda>s. A s \<and> B s, \<lambda>s. A' s \<and> B' s) (R \<and> S)"
by (clarsimp simp add: corres_case_helper_def)
private lemma corres_case_all_process:
"\<lbrakk> \<And>x. corres_case_helper (P, P') (Q x, Q' x) (R x) \<rbrakk>
\<Longrightarrow> corres_case_helper (P, P') (\<lambda>s. \<forall>x. Q x s, \<lambda>s. \<forall>x. Q' x s) (\<forall>x. R x)"
by (clarsimp simp: corres_case_helper_def subset_iff)
private lemma corres_case_all_process_weak:
"\<lbrakk> \<And>x. corres_case_helper (P, P') (Q x, Q') (R x) \<rbrakk>
\<Longrightarrow> corres_case_helper (P, P') (\<lambda>s. \<forall>x. Q x s, Q') (\<forall>x. R x)"
by (clarsimp simp: corres_case_helper_def subset_iff)
private lemma corres_case_imp_process:
"\<lbrakk> S \<Longrightarrow> corres_case_helper (P, P') (Q, Q') R \<rbrakk>
\<Longrightarrow> corres_case_helper (P, P') (\<lambda>s. S \<longrightarrow> Q s, \<lambda>s. S \<longrightarrow> Q' s) (S \<longrightarrow> R)"
by (clarsimp simp add: corres_case_helper_def subset_iff)
private lemma corres_case_imp_process_weak:
"\<lbrakk> S \<Longrightarrow> corres_case_helper (P, P') (Q, Q') R \<rbrakk>
\<Longrightarrow> corres_case_helper (P, P') (\<lambda>s. S \<longrightarrow> Q s, Q') (S \<longrightarrow> R)"
by (clarsimp simp add: corres_case_helper_def subset_iff)
private lemmas corres_case_process =
corres_case_conj_process corres_case_all_process corres_case_imp_process
private lemmas corres_case_process_weak =
corres_case_conj_process corres_case_all_process_weak corres_case_imp_process_weak
(* Turn goals of the form
(\<forall>y. x = SomeConstr y \<longrightarrow> corres (?P x) P' (SomeConstr y) g) \<and>
(\<forall>y. x = OtherConstr y \<longrightarrow> corres (?P x) P' (OtherConstr y) g) \<and>
...
into multiple goals of the form
\<And>y. x = SomeConstr y \<Longrightarrow> corres (?P1 x y) ?P'1 (SomeConstr y) g)
\<And>y. x = OtherConstr y \<Longrightarrow> corres (?P2 x y) ?P'2 (OtherConstr y) g)
with instantiations
?P x = \<lambda>s. (\<forall>y. x = SomeConstr y \<longrightarrow> ?P1 x y s) \<and> (\<forall>y. x = OtherConstr y \<longrightarrow> ?P2 x y s)
?P' = \<lambda>s. ?P'1 s \<and> ?P'2 s
We do this by first transforming the goal into a corres_case_helper goal, and then applying
the corresponding lifting rules. We first try to get both sides (?P and ?P') to have
quantifiers and implications to get a stronger statement, and fall back to the weaker \<and> for ?P'
shown above when that doesn't work (e.g. because ?P' might not depend on x).
When all lifting rules have applied, we transform the goal back into a corres goal using the
provided helper rule (e.g. corres_case_helper_corres_left below).
*)
private method corres_cases_body uses helper =
determ \<open>rule corres_case_helperI, repeat_new \<open>rule corres_case_process\<close>; rule helper
| rule corres_case_helperI, repeat_new \<open>rule corres_case_process_weak\<close>; rule helper\<close>
(* Instances of corres_case_helper for left and right side of the corres predicate.
These lemmas bind the corres guards to the corres_case_helper guards. *)
private lemma corres_case_helper_corres_left:
"corres_underlying sr nf nf' r Q Q' f f' \<Longrightarrow>
corres_case_helper (P, P') (Q, Q') (corres_underlying sr nf nf' r P P' f f')"
by (auto simp: corres_case_helper_def elim!: corres_guard_imp)
private lemma corres_case_helper_corres_right:
"corres_underlying sr nf nf' r Q' Q f f' \<Longrightarrow>
corres_case_helper (P, P') (Q, Q') (corres_underlying sr nf nf' r P' P f f')"
by (auto simp: corres_case_helper_def elim!: corres_guard_imp)
section \<open>Main method definitions\<close>
(* Case distinction on abstract side *)
method corres_cases_left =
determ \<open>
corres_pre,
(match conclusion in
"corres_underlying sr nf nf' r P P' (f >>= g) f'" for sr nf nf' r P P' f g f'
\<Rightarrow> \<open>apply_split f "\<lambda>f. corres_underlying sr nf nf' r P P' (f >>= g) f'"\<close>
\<bar> "corres_underlying sr nf nf' r P P' (f >>=E g) f'" for sr nf nf' r P P' f g f'
\<Rightarrow> \<open>apply_split f "\<lambda>f. corres_underlying sr nf nf' r P P' (f >>=E g) f'"\<close>
\<bar> "corres_underlying sr nf nf' r P P' f f'" for sr nf nf' r P P' f f'
\<Rightarrow> \<open>apply_split f "\<lambda>f. corres_underlying sr nf nf' r P P' f f'"\<close>),
corres_cases_body helper: corres_case_helper_corres_left\<close>
(* case distinction on concrete side *)
method corres_cases_right =
determ \<open>
corres_pre,
(match conclusion in
"corres_underlying sr nf nf' r P P' f (f' >>= g)" for sr nf nf' r P P' f g f'
\<Rightarrow> \<open>apply_split f' "\<lambda>f'. corres_underlying sr nf nf' r P P' f (f' >>= g)"\<close>
\<bar> "corres_underlying sr nf nf' r P P' f (f' >>=E g)" for sr nf nf' r P P' f g f'
\<Rightarrow> \<open>apply_split f' "\<lambda>f'. corres_underlying sr nf nf' r P P' f (f' >>=E g)"\<close>
\<bar> "corres_underlying sr nf nf' r P P' f f'" for sr nf nf' r P P' f f'
\<Rightarrow> \<open>apply_split f' "\<lambda>f'. corres_underlying sr nf nf' r P P' f f'"\<close>),
corres_cases_body helper: corres_case_helper_corres_right\<close>
(* single case distinction on either left or right, whichever works first *)
method corres_cases = corres_cases_left | corres_cases_right
(* Case distinction on abstract and concrete side with quadractic blowup, but attempt to solve
contradictory side conditions by simp. Cases that are solved by simp will produce \<top> as guard
so that no free schematics are introduced into later goals. *)
method corres_cases_both uses simp =
(* corres_pre first, so that the ";" later only refers to corres goals, not the final implications *)
determ \<open>
corres_pre,
(corres_cases_left; corres_cases_right;
(solves \<open>rule corres_inst[where P=\<top> and P'=\<top>], simp add: simp\<close>)?)\<close>
end
section \<open>Examples and tests\<close>
experiment
begin
(* abstract side *)
lemma "corres_underlying srel nf nf' rrel (G x) G' (case x of None \<Rightarrow> a | Some y \<Rightarrow> b y) m"
(* produces strong (forall, implies) guard conditions in the final implications for both sides *)
apply corres_cases
oops
schematic_goal
"\<And>x. corres_underlying srel nf nf' rrel (?G x) ?G' (case x of None \<Rightarrow> a | Some y \<Rightarrow> b y) m"
(* produces weak (just ?A \<and> ?B) guard conditions for concrete side, because ?G' does not
depend on "x", on which we do the case distinction *)
apply corres_cases
oops
(* abstract side, with bind *)
lemma "corres_underlying srel nf nf' rrel G G' ((case x of None \<Rightarrow> a | Some y \<Rightarrow> b y) >>= g) m"
apply corres_cases
oops
(* abstract side, with bindE *)
lemma "corres_underlying srel nf nf' rrel G G' ((case x of None \<Rightarrow> a | Some y \<Rightarrow> b y) >>=E g) m"
apply corres_cases
oops
(* concrete side: *)
lemma "corres_underlying srel nf nf' rrel G G' m (case x of None \<Rightarrow> a | Some y \<Rightarrow> b y)"
apply corres_cases
oops
schematic_goal
"\<And>x. corres_underlying srel nf nf' rrel ?G (?G' x) m (case x of None \<Rightarrow> a | Some y \<Rightarrow> b y)"
apply corres_cases
oops
lemma "corres_underlying srel nf nf' rrel G G' m ((case x of None \<Rightarrow> a | Some y \<Rightarrow> b y) >>= g)"
apply corres_cases
oops
lemma "corres_underlying srel nf nf' rrel G G' m ((case x of None \<Rightarrow> a | Some y \<Rightarrow> b y) >>=E g)"
apply corres_cases
oops
(* both sides: *)
lemma "corres_underlying srel nf nf' rrel G G' (case x of None \<Rightarrow> a | Some y \<Rightarrow> b)
(case x of None \<Rightarrow> a' | Some y \<Rightarrow> b' y)"
(* two cases remain (both None, both Some); eliminated cases have guard \<top> in final implication *)
apply corres_cases_both
oops
schematic_goal
"\<And>x y. corres_underlying srel nf nf' rrel (?G x) (?G' y) (case x of None \<Rightarrow> a | Some y \<Rightarrow> b)
(case y of None \<Rightarrow> a' | Some y \<Rightarrow> b' y)"
(* 4 cases remain, because none are contradictory *)
apply corres_cases_both
oops
(* some example relation between abstract and concrete values *)
definition
"none_rel x y \<equiv> (x = None) = (y = None)"
lemma
"none_rel x y \<Longrightarrow>
corres_underlying srel nf nf' rrel G G' (case x of None \<Rightarrow> a | Some y \<Rightarrow> b)
(case y of None \<Rightarrow> a' | Some y \<Rightarrow> b' y)"
(* two cases remain, none_rel is untouched in the cases that remain, but unfolded in the
ones that were eliminated *)
apply (corres_cases_both simp: none_rel_def)
oops
end
end