Mercurial > cpdt > repo
comparison src/Reflection.v @ 360:e0d91bcf70ec
Pass through old content of Reflection
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Wed, 02 Nov 2011 14:48:25 -0400 |
parents | 3322367e955d |
children | df17b699a04f |
comparison
equal
deleted
inserted
replaced
359:059c51227e69 | 360:e0d91bcf70ec |
---|---|
16 (* end hide *) | 16 (* end hide *) |
17 | 17 |
18 | 18 |
19 (** %\chapter{Proof by Reflection}% *) | 19 (** %\chapter{Proof by Reflection}% *) |
20 | 20 |
21 (** The last chapter highlighted a very heuristic approach to proving. In this chapter, we will study an alternative technique, %\textit{%#<i>#proof by reflection#</i>#%}%. We will write, in Gallina, decision procedures with proofs of correctness, and we will appeal to these procedures in writing very short proofs. Such a proof is checked by running the decision procedure. The term %\textit{%#<i>#reflection#</i>#%}% applies because we will need to translate Gallina propositions into values of inductive types representing syntax, so that Gallina programs may analyze them. *) | 21 (** The last chapter highlighted a very heuristic approach to proving. In this chapter, we will study an alternative technique, %\index{proof by reflection}\textit{%#<i>#proof by reflection#</i>#%}~\cite{reflection}%. We will write, in Gallina, decision procedures with proofs of correctness, and we will appeal to these procedures in writing very short proofs. Such a proof is checked by running the decision procedure. The term %\textit{%#<i>#reflection#</i>#%}% applies because we will need to translate Gallina propositions into values of inductive types representing syntax, so that Gallina programs may analyze them. *) |
22 | 22 |
23 | 23 |
24 (** * Proving Evenness *) | 24 (** * Proving Evenness *) |
25 | 25 |
26 (** Proving that particular natural number constants are even is certainly something we would rather have happen automatically. The Ltac-programming techniques that we learned in the last chapter make it easy to implement such a procedure. *) | 26 (** Proving that particular natural number constants are even is certainly something we would rather have happen automatically. The Ltac-programming techniques that we learned in the last chapter make it easy to implement such a procedure. *) |
45 (Even_SS | 45 (Even_SS |
46 (Even_SS | 46 (Even_SS |
47 | 47 |
48 ]] | 48 ]] |
49 | 49 |
50 %\noindent%...and so on. This procedure always works (at least on machines with infinite resources), but it has a serious drawback, which we see when we print the proof it generates that 256 is even. The final proof term has length super-linear in the input value. (Coq's implicit arguments mechanism is hiding the values given for parameter [n] of [Even_SS], which is why the proof term only appears linear here.) This seems like a shame, since we could write a trivial and trustworthy program to verify evenness of constants. The proof checker could simply call our program where needed. | 50 %\noindent%...and so on. This procedure always works (at least on machines with infinite resources), but it has a serious drawback, which we see when we print the proof it generates that 256 is even. The final proof term has length super-linear in the input value. Coq's implicit arguments mechanism is hiding the values given for parameter [n] of [Even_SS], which is why the proof term only appears linear here. Also, proof terms are represented internally as syntax trees, with opportunity for sharing of node representations, but in this chapter we will measure proof term size as simple textual length or as the number of nodes in the term's syntax tree, two measures that are approximately equivalent. Sometimes apparently large proof terms have enough internal sharing that they take up less memory than we expect, but one avoids having to reason about such sharing by ensuring that the size of a sharing-free version of a term is low enough. |
51 | |
52 Superlinear evenness proof terms seem like a shame, since we could write a trivial and trustworthy program to verify evenness of constants. The proof checker could simply call our program where needed. | |
51 | 53 |
52 It is also unfortunate not to have static typing guarantees that our tactic always behaves appropriately. Other invocations of similar tactics might fail with dynamic type errors, and we would not know about the bugs behind these errors until we happened to attempt to prove complex enough goals. | 54 It is also unfortunate not to have static typing guarantees that our tactic always behaves appropriately. Other invocations of similar tactics might fail with dynamic type errors, and we would not know about the bugs behind these errors until we happened to attempt to prove complex enough goals. |
53 | 55 |
54 The techniques of proof by reflection address both complaints. We will be able to write proofs like this with constant size overhead beyond the size of the input, and we will do it with verified decision procedures written in Gallina. | 56 The techniques of proof by reflection address both complaints. We will be able to write proofs like this with constant size overhead beyond the size of the input, and we will do it with verified decision procedures written in Gallina. |
55 | 57 |
77 | 1 => No | 79 | 1 => No |
78 | S (S n') => Reduce (F n') | 80 | S (S n') => Reduce (F n') |
79 end); auto. | 81 end); auto. |
80 Defined. | 82 Defined. |
81 | 83 |
82 (** We can use dependent pattern-matching to write a function that performs a surprising feat. When given a [partial P], this function [partialOut] returns a proof of [P] if the [partial] value contains a proof, and it returns a (useless) proof of [True] otherwise. From the standpoint of ML and Haskell programming, it seems impossible to write such a type, but it is trivial with a [return] annotation. *) | 84 (** The function [check_even] may be viewed as a %\emph{%#<i>#verified decision procedure#</i>#%}%, because its type guarantees that it never returns [Yes] for inputs that are not even. |
85 | |
86 Now we can use dependent pattern-matching to write a function that performs a surprising feat. When given a [partial P], this function [partialOut] returns a proof of [P] if the [partial] value contains a proof, and it returns a (useless) proof of [True] otherwise. From the standpoint of ML and Haskell programming, it seems impossible to write such a type, but it is trivial with a [return] annotation. *) | |
83 | 87 |
84 Definition partialOut (P : Prop) (x : [P]) := | 88 Definition partialOut (P : Prop) (x : [P]) := |
85 match x return (match x with | 89 match x return (match x with |
86 | Proved _ => P | 90 | Proved _ => P |
87 | Uncertain => True | 91 | Uncertain => True |
96 match goal with | 100 match goal with |
97 | [ |- isEven ?N] => exact (partialOut (check_even N)) | 101 | [ |- isEven ?N] => exact (partialOut (check_even N)) |
98 end. | 102 end. |
99 (* end thide *) | 103 (* end thide *) |
100 | 104 |
101 (** We identify which natural number we are considering, and we %``%#"#prove#"#%''% its evenness by pulling the proof out of the appropriate [check_even] call. *) | 105 (** We identify which natural number we are considering, and we %``%#"#prove#"#%''% its evenness by pulling the proof out of the appropriate [check_even] call. Recall that the %\index{tactics!exact}%[exact] tactic proves a proposition [P] when given a proof term of precisely type [P]. *) |
102 | 106 |
103 Theorem even_256' : isEven 256. | 107 Theorem even_256' : isEven 256. |
104 prove_even_reflective. | 108 prove_even_reflective. |
105 Qed. | 109 Qed. |
106 | 110 |
107 Print even_256'. | 111 Print even_256'. |
108 (** %\vspace{-.15in}% [[ | 112 (** %\vspace{-.15in}% [[ |
109 even_256' = partialOut (check_even 256) | 113 even_256' = partialOut (check_even 256) |
110 : isEven 256 | 114 : isEven 256 |
111 | |
112 ]] | 115 ]] |
113 | 116 |
114 We can see a constant wrapper around the object of the proof. For any even number, this form of proof will suffice. The size of the proof term is now linear in the number being checked, containing two repetitions of the unary form of that number, one of which is hidden above within the implicit argument to [partialOut]. | 117 We can see a constant wrapper around the object of the proof. For any even number, this form of proof will suffice. The size of the proof term is now linear in the number being checked, containing two repetitions of the unary form of that number, one of which is hidden above within the implicit argument to [partialOut]. |
115 | 118 |
116 What happens if we try the tactic with an odd number? *) | 119 What happens if we try the tactic with an odd number? *) |
117 | 120 |
118 Theorem even_255 : isEven 255. | 121 Theorem even_255 : isEven 255. |
119 (** [[ | 122 (** %\vspace{-.275in}%[[ |
120 prove_even_reflective. | 123 prove_even_reflective. |
121 | 124 ]] |
125 | |
126 << | |
122 User error: No matching clauses for match goal | 127 User error: No matching clauses for match goal |
123 | 128 >> |
124 ]] | |
125 | 129 |
126 Thankfully, the tactic fails. To see more precisely what goes wrong, we can run manually the body of the [match]. | 130 Thankfully, the tactic fails. To see more precisely what goes wrong, we can run manually the body of the [match]. |
127 | 131 |
128 [[ | 132 %\vspace{-.15in}%[[ |
129 exact (partialOut (check_even 255)). | 133 exact (partialOut (check_even 255)). |
130 | 134 ]] |
135 | |
136 << | |
131 Error: The term "partialOut (check_even 255)" has type | 137 Error: The term "partialOut (check_even 255)" has type |
132 "match check_even 255 with | 138 "match check_even 255 with |
133 | Yes => isEven 255 | 139 | Yes => isEven 255 |
134 | No => True | 140 | No => True |
135 end" while it is expected to have type "isEven 255" | 141 end" while it is expected to have type "isEven 255" |
136 | 142 >> |
137 ]] | 143 |
138 | 144 As usual, the type checker performs no reductions to simplify error messages. If we reduced the first term ourselves, we would see that [check_even 255] reduces to a [No], so that the first term is equivalent to [True], which certainly does not unify with [isEven 255]. *) |
139 As usual, the type-checker performs no reductions to simplify error messages. If we reduced the first term ourselves, we would see that [check_even 255] reduces to a [No], so that the first term is equivalent to [True], which certainly does not unify with [isEven 255]. *) | |
140 | 145 |
141 Abort. | 146 Abort. |
142 | 147 |
143 | 148 (** Our tactic [prove_even_reflective] is reflective because it performs a proof search process (a trivial one, in this case) wholly within Gallina, where the only use of Ltac is to translate a goal into an appropriate use of [check_even]. *) |
144 (** * Reflecting the Syntax of a Trivial Tautology Language *) | 149 |
150 | |
151 (** * Reifying the Syntax of a Trivial Tautology Language *) | |
145 | 152 |
146 (** We might also like to have reflective proofs of trivial tautologies like this one: *) | 153 (** We might also like to have reflective proofs of trivial tautologies like this one: *) |
147 | 154 |
148 Theorem true_galore : (True /\ True) -> (True \/ (True /\ (True -> True))). | 155 Theorem true_galore : (True /\ True) -> (True \/ (True /\ (True -> True))). |
149 tauto. | 156 tauto. |
158 | 165 |
159 ]] | 166 ]] |
160 | 167 |
161 As we might expect, the proof that [tauto] builds contains explicit applications of natural deduction rules. For large formulas, this can add a linear amount of proof size overhead, beyond the size of the input. | 168 As we might expect, the proof that [tauto] builds contains explicit applications of natural deduction rules. For large formulas, this can add a linear amount of proof size overhead, beyond the size of the input. |
162 | 169 |
163 To write a reflective procedure for this class of goals, we will need to get into the actual %``%#"#reflection#"#%''% part of %``%#"#proof by reflection.#"#%''% It is impossible to case-analyze a [Prop] in any way in Gallina. We must %\textit{%#<i>#reflect#</i>#%}% [Prop] into some type that we %\textit{%#<i>#can#</i>#%}% analyze. This inductive type is a good candidate: *) | 170 To write a reflective procedure for this class of goals, we will need to get into the actual %``%#"#reflection#"#%''% part of %``%#"#proof by reflection.#"#%''% It is impossible to case-analyze a [Prop] in any way in Gallina. We must %\index{reification}\textit{%#<i>#reify#</i>#%}% [Prop] into some type that we %\textit{%#<i>#can#</i>#%}% analyze. This inductive type is a good candidate: *) |
164 | 171 |
165 (* begin thide *) | 172 (* begin thide *) |
166 Inductive taut : Set := | 173 Inductive taut : Set := |
167 | TautTrue : taut | 174 | TautTrue : taut |
168 | TautAnd : taut -> taut -> taut | 175 | TautAnd : taut -> taut -> taut |
169 | TautOr : taut -> taut -> taut | 176 | TautOr : taut -> taut -> taut |
170 | TautImp : taut -> taut -> taut. | 177 | TautImp : taut -> taut -> taut. |
171 | 178 |
172 (** We write a recursive function to %``%#"#unreflect#"#%''% this syntax back to [Prop]. *) | 179 (** We write a recursive function to %\emph{%#<i>#reflect#</i>#%}% this syntax back to [Prop]. Such functions are also called %\index{interpretation function}\emph{%#<i>#interpretation functions#</i>#%}%, and have used them in previous examples to give semantics to small programming languages. *) |
173 | 180 |
174 Fixpoint tautDenote (t : taut) : Prop := | 181 Fixpoint tautDenote (t : taut) : Prop := |
175 match t with | 182 match t with |
176 | TautTrue => True | 183 | TautTrue => True |
177 | TautAnd t1 t2 => tautDenote t1 /\ tautDenote t2 | 184 | TautAnd t1 t2 => tautDenote t1 /\ tautDenote t2 |
183 | 190 |
184 Theorem tautTrue : forall t, tautDenote t. | 191 Theorem tautTrue : forall t, tautDenote t. |
185 induction t; crush. | 192 induction t; crush. |
186 Qed. | 193 Qed. |
187 | 194 |
188 (** To use [tautTrue] to prove particular formulas, we need to implement the syntax reflection process. A recursive Ltac function does the job. *) | 195 (** To use [tautTrue] to prove particular formulas, we need to implement the syntax reification process. A recursive Ltac function does the job. *) |
189 | 196 |
190 Ltac tautReflect P := | 197 Ltac tautReify P := |
191 match P with | 198 match P with |
192 | True => TautTrue | 199 | True => TautTrue |
193 | ?P1 /\ ?P2 => | 200 | ?P1 /\ ?P2 => |
194 let t1 := tautReflect P1 in | 201 let t1 := tautReify P1 in |
195 let t2 := tautReflect P2 in | 202 let t2 := tautReify P2 in |
196 constr:(TautAnd t1 t2) | 203 constr:(TautAnd t1 t2) |
197 | ?P1 \/ ?P2 => | 204 | ?P1 \/ ?P2 => |
198 let t1 := tautReflect P1 in | 205 let t1 := tautReify P1 in |
199 let t2 := tautReflect P2 in | 206 let t2 := tautReify P2 in |
200 constr:(TautOr t1 t2) | 207 constr:(TautOr t1 t2) |
201 | ?P1 -> ?P2 => | 208 | ?P1 -> ?P2 => |
202 let t1 := tautReflect P1 in | 209 let t1 := tautReify P1 in |
203 let t2 := tautReflect P2 in | 210 let t2 := tautReify P2 in |
204 constr:(TautImp t1 t2) | 211 constr:(TautImp t1 t2) |
205 end. | 212 end. |
206 | 213 |
207 (** With [tautReflect] available, it is easy to finish our reflective tactic. We look at the goal formula, reflect it, and apply [tautTrue] to the reflected formula. *) | 214 (** With [tautReify] available, it is easy to finish our reflective tactic. We look at the goal formula, reflect it, and apply [tautTrue] to the reflected formula. *) |
208 | 215 |
209 Ltac obvious := | 216 Ltac obvious := |
210 match goal with | 217 match goal with |
211 | [ |- ?P ] => | 218 | [ |- ?P ] => |
212 let t := tautReflect P in | 219 let t := tautReify P in |
213 exact (tautTrue t) | 220 exact (tautTrue t) |
214 end. | 221 end. |
215 | 222 |
216 (** We can verify that [obvious] solves our original example, with a proof term that does not mention details of the proof. *) | 223 (** We can verify that [obvious] solves our original example, with a proof term that does not mention details of the proof. *) |
217 (* end thide *) | 224 (* end thide *) |
226 true_galore' = | 233 true_galore' = |
227 tautTrue | 234 tautTrue |
228 (TautImp (TautAnd TautTrue TautTrue) | 235 (TautImp (TautAnd TautTrue TautTrue) |
229 (TautOr TautTrue (TautAnd TautTrue (TautImp TautTrue TautTrue)))) | 236 (TautOr TautTrue (TautAnd TautTrue (TautImp TautTrue TautTrue)))) |
230 : True /\ True -> True \/ True /\ (True -> True) | 237 : True /\ True -> True \/ True /\ (True -> True) |
231 | 238 ]] |
232 ]] | 239 |
233 | 240 It is worth considering how the reflective tactic improves on a pure-Ltac implementation. The formula reification process is just as ad-hoc as before, so we gain little there. In general, proofs will be more complicated than formula translation, and the %``%#"#generic proof rule#"#%''% that we apply here %\textit{%#<i>#is#</i>#%}% on much better formal footing than a recursive Ltac function. The dependent type of the proof guarantees that it %``%#"#works#"#%''% on any input formula. This is all in addition to the proof-size improvement that we have already seen. |
234 It is worth considering how the reflective tactic improves on a pure-Ltac implementation. The formula reflection process is just as ad-hoc as before, so we gain little there. In general, proofs will be more complicated than formula translation, and the %``%#"#generic proof rule#"#%''% that we apply here %\textit{%#<i>#is#</i>#%}% on much better formal footing than a recursive Ltac function. The dependent type of the proof guarantees that it %``%#"#works#"#%''% on any input formula. This is all in addition to the proof-size improvement that we have already seen. *) | 241 |
242 It may also be worth pointing out that our previous example of evenness testing used a function [partialOut] for sound handling of input goals that the verified decision procedure fails to prove. Here, we prove that our procedure [tautTrue] (recall that an inductive proof may be viewed as a recursive procedure) is able to prove any goal representable in [taut], so no extra step is necessary. *) | |
235 | 243 |
236 | 244 |
237 (** * A Monoid Expression Simplifier *) | 245 (** * A Monoid Expression Simplifier *) |
238 | 246 |
239 (** Proof by reflection does not require encoding of all of the syntax in a goal. We can insert %``%#"#variables#"#%''% in our syntax types to allow injection of arbitrary pieces, even if we cannot apply specialized reasoning to them. In this section, we explore that possibility by writing a tactic for normalizing monoid equations. *) | 247 (** Proof by reflection does not require encoding of all of the syntax in a goal. We can insert %``%#"#variables#"#%''% in our syntax types to allow injection of arbitrary pieces, even if we cannot apply specialized reasoning to them. In this section, we explore that possibility by writing a tactic for normalizing monoid equations. *) |
257 Inductive mexp : Set := | 265 Inductive mexp : Set := |
258 | Ident : mexp | 266 | Ident : mexp |
259 | Var : A -> mexp | 267 | Var : A -> mexp |
260 | Op : mexp -> mexp -> mexp. | 268 | Op : mexp -> mexp -> mexp. |
261 | 269 |
262 (** Next, we write an %``%#"#un-reflect#"#%''% function. *) | 270 (** Next, we write an interpretation function. *) |
263 | 271 |
264 Fixpoint mdenote (me : mexp) : A := | 272 Fixpoint mdenote (me : mexp) : A := |
265 match me with | 273 match me with |
266 | Ident => e | 274 | Ident => e |
267 | Var v => v | 275 | Var v => v |
304 mldenote (flatten me1) = mldenote (flatten me2) | 312 mldenote (flatten me1) = mldenote (flatten me2) |
305 -> mdenote me1 = mdenote me2. | 313 -> mdenote me1 = mdenote me2. |
306 intros; repeat rewrite flatten_correct; assumption. | 314 intros; repeat rewrite flatten_correct; assumption. |
307 Qed. | 315 Qed. |
308 | 316 |
309 (** We implement reflection into the [mexp] type. *) | 317 (** We implement reification into the [mexp] type. *) |
310 | 318 |
311 Ltac reflect me := | 319 Ltac reify me := |
312 match me with | 320 match me with |
313 | e => Ident | 321 | e => Ident |
314 | ?me1 + ?me2 => | 322 | ?me1 + ?me2 => |
315 let r1 := reflect me1 in | 323 let r1 := reify me1 in |
316 let r2 := reflect me2 in | 324 let r2 := reify me2 in |
317 constr:(Op r1 r2) | 325 constr:(Op r1 r2) |
318 | _ => constr:(Var me) | 326 | _ => constr:(Var me) |
319 end. | 327 end. |
320 | 328 |
321 (** The final [monoid] tactic works on goals that equate two monoid terms. We reflect each and change the goal to refer to the reflected versions, finishing off by applying [monoid_reflect] and simplifying uses of [mldenote]. *) | 329 (** The final [monoid] tactic works on goals that equate two monoid terms. We reify each and change the goal to refer to the reified versions, finishing off by applying [monoid_reflect] and simplifying uses of [mldenote]. Recall that the %\index{tactics!change}%[change] tactic replaces a conclusion formula with another that is definitionally equal to it. *) |
322 | 330 |
323 Ltac monoid := | 331 Ltac monoid := |
324 match goal with | 332 match goal with |
325 | [ |- ?me1 = ?me2 ] => | 333 | [ |- ?me1 = ?me2 ] => |
326 let r1 := reflect me1 in | 334 let r1 := reify me1 in |
327 let r2 := reflect me2 in | 335 let r2 := reify me2 in |
328 change (mdenote r1 = mdenote r2); | 336 change (mdenote r1 = mdenote r2); |
329 apply monoid_reflect; simpl mldenote | 337 apply monoid_reflect; simpl |
330 end. | 338 end. |
331 | 339 |
332 (** We can make short work of theorems like this one: *) | 340 (** We can make short work of theorems like this one: *) |
333 | 341 |
334 (* end thide *) | 342 (* end thide *) |
339 ============================ | 347 ============================ |
340 a + (b + (c + (d + e))) = a + (b + (c + (d + e))) | 348 a + (b + (c + (d + e))) = a + (b + (c + (d + e))) |
341 | 349 |
342 ]] | 350 ]] |
343 | 351 |
344 [monoid] has canonicalized both sides of the equality, such that we can finish the proof by reflexivity. *) | 352 Our tactic has canonicalized both sides of the equality, such that we can finish the proof by reflexivity. *) |
345 | 353 |
346 reflexivity. | 354 reflexivity. |
347 Qed. | 355 Qed. |
348 | 356 |
349 (** It is interesting to look at the form of the proof. *) | 357 (** It is interesting to look at the form of the proof. *) |
354 fun a b c d : A => | 362 fun a b c d : A => |
355 monoid_reflect (Op (Op (Op (Var a) (Var b)) (Var c)) (Var d)) | 363 monoid_reflect (Op (Op (Op (Var a) (Var b)) (Var c)) (Var d)) |
356 (Op (Op (Var a) (Op (Var b) (Var c))) (Var d)) | 364 (Op (Op (Var a) (Op (Var b) (Var c))) (Var d)) |
357 (refl_equal (a + (b + (c + (d + e))))) | 365 (refl_equal (a + (b + (c + (d + e))))) |
358 : forall a b c d : A, a + b + c + d = a + (b + c) + d | 366 : forall a b c d : A, a + b + c + d = a + (b + c) + d |
359 | |
360 ]] | 367 ]] |
361 | 368 |
362 The proof term contains only restatements of the equality operands in reflected form, followed by a use of reflexivity on the shared canonical form. *) | 369 The proof term contains only restatements of the equality operands in reified form, followed by a use of reflexivity on the shared canonical form. *) |
363 | 370 |
364 End monoid. | 371 End monoid. |
365 | 372 |
366 (** Extensions of this basic approach are used in the implementations of the [ring] and [field] tactics that come packaged with Coq. *) | 373 (** Extensions of this basic approach are used in the implementations of the %\index{tactics!ring}%[ring] and %\index{tactics!field}%[field] tactics that come packaged with Coq. *) |
367 | 374 |
368 | 375 |
369 (** * A Smarter Tautology Solver *) | 376 (** * A Smarter Tautology Solver *) |
370 | 377 |
371 (** Now we are ready to revisit our earlier tautology solver example. We want to broaden the scope of the tactic to include formulas whose truth is not syntactically apparent. We will want to allow injection of arbitrary formulas, like we allowed arbitrary monoid expressions in the last example. Since we are working in a richer theory, it is important to be able to use equalities between different injected formulas. For instance, we cannot prove [P -> P] by translating the formula into a value like [Imp (Var P) (Var P)], because a Gallina function has no way of comparing the two [P]s for equality. | 378 (** Now we are ready to revisit our earlier tautology solver example. We want to broaden the scope of the tactic to include formulas whose truth is not syntactically apparent. We will want to allow injection of arbitrary formulas, like we allowed arbitrary monoid expressions in the last example. Since we are working in a richer theory, it is important to be able to use equalities between different injected formulas. For instance, we cannot prove [P -> P] by translating the formula into a value like [Imp (][Var P) (][Var P)], because a Gallina function has no way of comparing the two [P]s for equality. |
372 | 379 |
373 To arrive at a nice implementation satisfying these criteria, we introduce the [quote] tactic and its associated library. *) | 380 To arrive at a nice implementation satisfying these criteria, we introduce the %\index{tactics!quote}%[quote] tactic and its associated library. *) |
374 | 381 |
375 Require Import Quote. | 382 Require Import Quote. |
376 | 383 |
377 (* begin thide *) | 384 (* begin thide *) |
378 Inductive formula : Set := | 385 Inductive formula : Set := |
381 | Falsehood : formula | 388 | Falsehood : formula |
382 | And : formula -> formula -> formula | 389 | And : formula -> formula -> formula |
383 | Or : formula -> formula -> formula | 390 | Or : formula -> formula -> formula |
384 | Imp : formula -> formula -> formula. | 391 | Imp : formula -> formula -> formula. |
385 | 392 |
386 (** The type [index] comes from the [Quote] library and represents a countable variable type. The rest of [formula]'s definition should be old hat by now. | 393 (** The type %\index{Gallina terms!index}%[index] comes from the [Quote] library and represents a countable variable type. The rest of [formula]'s definition should be old hat by now. |
387 | 394 |
388 The [quote] tactic will implement injection from [Prop] into [formula] for us, but it is not quite as smart as we might like. In particular, it interprets implications incorrectly, so we will need to declare a wrapper definition for implication, as we did in the last chapter. *) | 395 The [quote] tactic will implement injection from [Prop] into [formula] for us, but it is not quite as smart as we might like. In particular, it interprets implications incorrectly, so we will need to declare a wrapper definition for implication, as we did in the last chapter. *) |
389 | 396 |
390 Definition imp (P1 P2 : Prop) := P1 -> P2. | 397 Definition imp (P1 P2 : Prop) := P1 -> P2. |
391 Infix "-->" := imp (no associativity, at level 95). | 398 Infix "-->" := imp (no associativity, at level 95). |
402 | And f1 f2 => formulaDenote atomics f1 /\ formulaDenote atomics f2 | 409 | And f1 f2 => formulaDenote atomics f1 /\ formulaDenote atomics f2 |
403 | Or f1 f2 => formulaDenote atomics f1 \/ formulaDenote atomics f2 | 410 | Or f1 f2 => formulaDenote atomics f1 \/ formulaDenote atomics f2 |
404 | Imp f1 f2 => formulaDenote atomics f1 --> formulaDenote atomics f2 | 411 | Imp f1 f2 => formulaDenote atomics f1 --> formulaDenote atomics f2 |
405 end. | 412 end. |
406 | 413 |
407 (** The [varmap] type family implements maps from [index] values. In this case, we define an assignment as a map from variables to [Prop]s. [formulaDenote] works with an assignment, and we use the [varmap_find] function to consult the assignment in the [Atomic] case. The first argument to [varmap_find] is a default value, in case the variable is not found. *) | 414 (** The %\index{Gallina terms!varmap}%[varmap] type family implements maps from [index] values. In this case, we define an assignment as a map from variables to [Prop]s. Our reifier [formulaDenote] works with an assignment, and we use the [varmap_find] function to consult the assignment in the [Atomic] case. The first argument to [varmap_find] is a default value, in case the variable is not found. *) |
408 | 415 |
409 Section my_tauto. | 416 Section my_tauto. |
410 Variable atomics : asgn. | 417 Variable atomics : asgn. |
411 | 418 |
412 Definition holds (v : index) := varmap_find False v atomics. | 419 Definition holds (v : index) := varmap_find False v atomics. |
501 Definition my_tauto : forall f : formula, [formulaDenote atomics f]. | 508 Definition my_tauto : forall f : formula, [formulaDenote atomics f]. |
502 intro; refine (Reduce (backward nil f)); crush. | 509 intro; refine (Reduce (backward nil f)); crush. |
503 Defined. | 510 Defined. |
504 End my_tauto. | 511 End my_tauto. |
505 | 512 |
506 (** Our final tactic implementation is now fairly straightforward. First, we [intro] all quantifiers that do not bind [Prop]s. Then we call the [quote] tactic, which implements the reflection for us. Finally, we are able to construct an exact proof via [partialOut] and the [my_tauto] Gallina function. *) | 513 (** Our final tactic implementation is now fairly straightforward. First, we [intro] all quantifiers that do not bind [Prop]s. Then we call the [quote] tactic, which implements the reification for us. Finally, we are able to construct an exact proof via [partialOut] and the [my_tauto] Gallina function. *) |
507 | 514 |
508 Ltac my_tauto := | 515 Ltac my_tauto := |
509 repeat match goal with | 516 repeat match goal with |
510 | [ |- forall x : ?P, _ ] => | 517 | [ |- forall x : ?P, _ ] => |
511 match type of P with | 518 match type of P with |
527 | 534 |
528 Print mt1. | 535 Print mt1. |
529 (** %\vspace{-.15in}% [[ | 536 (** %\vspace{-.15in}% [[ |
530 mt1 = partialOut (my_tauto (Empty_vm Prop) Truth) | 537 mt1 = partialOut (my_tauto (Empty_vm Prop) Truth) |
531 : True | 538 : True |
532 | |
533 ]] | 539 ]] |
534 | 540 |
535 We see [my_tauto] applied with an empty [varmap], since every subformula is handled by [formulaDenote]. *) | 541 We see [my_tauto] applied with an empty [varmap], since every subformula is handled by [formulaDenote]. *) |
536 | 542 |
537 Theorem mt2 : forall x y : nat, x = y --> x = y. | 543 Theorem mt2 : forall x y : nat, x = y --> x = y. |
544 fun x y : nat => | 550 fun x y : nat => |
545 partialOut | 551 partialOut |
546 (my_tauto (Node_vm (x = y) (Empty_vm Prop) (Empty_vm Prop)) | 552 (my_tauto (Node_vm (x = y) (Empty_vm Prop) (Empty_vm Prop)) |
547 (Imp (Atomic End_idx) (Atomic End_idx))) | 553 (Imp (Atomic End_idx) (Atomic End_idx))) |
548 : forall x y : nat, x = y --> x = y | 554 : forall x y : nat, x = y --> x = y |
549 | |
550 ]] | 555 ]] |
551 | 556 |
552 Crucially, both instances of [x = y] are represented with the same index, [End_idx]. The value of this index only needs to appear once in the [varmap], whose form reveals that [varmap]s are represented as binary trees, where [index] values denote paths from tree roots to leaves. *) | 557 Crucially, both instances of [x = y] are represented with the same index, [End_idx]. The value of this index only needs to appear once in the [varmap], whose form reveals that [varmap]s are represented as binary trees, where [index] values denote paths from tree roots to leaves. *) |
553 | 558 |
554 Theorem mt3 : forall x y z, | 559 Theorem mt3 : forall x y z, |
569 (And (Atomic (Right_idx End_idx)) (Atomic End_idx))) | 574 (And (Atomic (Right_idx End_idx)) (Atomic End_idx))) |
570 (And (Atomic (Right_idx End_idx)) | 575 (And (Atomic (Right_idx End_idx)) |
571 (Or (Atomic (Left_idx End_idx)) (Atomic End_idx))))) | 576 (Or (Atomic (Left_idx End_idx)) (Atomic End_idx))))) |
572 : forall x y z : nat, | 577 : forall x y z : nat, |
573 x < y /\ y > z \/ y > z /\ x < S y --> y > z /\ (x < y \/ x < S y) | 578 x < y /\ y > z \/ y > z /\ x < S y --> y > z /\ (x < y \/ x < S y) |
574 | |
575 ]] | 579 ]] |
576 | 580 |
577 Our goal contained three distinct atomic formulas, and we see that a three-element [varmap] is generated. | 581 Our goal contained three distinct atomic formulas, and we see that a three-element [varmap] is generated. |
578 | 582 |
579 It can be interesting to observe differences between the level of repetition in proof terms generated by [my_tauto] and [tauto] for especially trivial theorems. *) | 583 It can be interesting to observe differences between the level of repetition in proof terms generated by [my_tauto] and [tauto] for especially trivial theorems. *) |
616 (fun (_ : True) (H9 : True /\ False) => | 620 (fun (_ : True) (H9 : True /\ False) => |
617 and_ind (fun (_ : True) (H11 : False) => False_ind False H11) | 621 and_ind (fun (_ : True) (H11 : False) => False_ind False H11) |
618 H9) H7) H5) H3) H1) H | 622 H9) H7) H5) H3) H1) H |
619 : True /\ True /\ True /\ True /\ True /\ True /\ False -> False | 623 : True /\ True /\ True /\ True /\ True /\ True /\ False -> False |
620 ]] | 624 ]] |
621 *) | 625 |
626 The traditional [tauto] tactic introduces a quadratic blow-up in the size of the proof term, whereas proofs produced by [my_tauto] always have linear size. *) | |
622 | 627 |
623 | 628 |
624 (** * Exercises *) | 629 (** * Exercises *) |
625 | 630 |
626 (** remove printing * *) | 631 (** remove printing * *) |
628 (** %\begin{enumerate}%#<ol># | 633 (** %\begin{enumerate}%#<ol># |
629 | 634 |
630 %\item%#<li># Implement a reflective procedure for normalizing systems of linear equations over rational numbers. In particular, the tactic should identify all hypotheses that are linear equations over rationals where the equation righthand sides are constants. It should normalize each hypothesis to have a lefthand side that is a sum of products of constants and variables, with no variable appearing multiple times. Then, your tactic should add together all of these equations to form a single new equation, possibly clearing the original equations. Some coefficients may cancel in the addition, reducing the number of variables that appear. | 635 %\item%#<li># Implement a reflective procedure for normalizing systems of linear equations over rational numbers. In particular, the tactic should identify all hypotheses that are linear equations over rationals where the equation righthand sides are constants. It should normalize each hypothesis to have a lefthand side that is a sum of products of constants and variables, with no variable appearing multiple times. Then, your tactic should add together all of these equations to form a single new equation, possibly clearing the original equations. Some coefficients may cancel in the addition, reducing the number of variables that appear. |
631 | 636 |
632 To work with rational numbers, import module [QArith] and use [Local Open Scope Q_scope]. All of the usual arithmetic operator notations will then work with rationals, and there are shorthands for constants 0 and 1. Other rationals must be written as [num # den] for numerator [num] and denominator [den]. Use the infix operator [==] in place of [=], to deal with different ways of expressing the same number as a fraction. For instance, a theorem and proof like this one should work with your tactic: | 637 To work with rational numbers, import module [QArith] and use [Local Open Scope Q_scope]. All of the usual arithmetic operator notations will then work with rationals, and there are shorthands for constants 0 and 1. Other rationals must be written as [num # den] for numerator [num] and denominator [den]. Use the infix operator [==] in place of [=], to deal with different ways of expressing the same number as a fraction. For instance, a theorem and proof like this one should work with your tactic: |
633 | |
634 [[ | 638 [[ |
635 Theorem t2 : forall x y z, (2 # 1) * (x - (3 # 2) * y) == 15 # 1 | 639 Theorem t2 : forall x y z, (2 # 1) * (x - (3 # 2) * y) == 15 # 1 |
636 -> z + (8 # 1) * x == 20 # 1 | 640 -> z + (8 # 1) * x == 20 # 1 |
637 -> (-6 # 2) * y + (10 # 1) * x + z == 35 # 1. | 641 -> (-6 # 2) * y + (10 # 1) * x + z == 35 # 1. |
638 intros; reflectContext; assumption. | 642 intros; reifyContext; assumption. |
639 Qed. | 643 Qed. |
640 | |
641 ]] | 644 ]] |
642 | 645 |
643 Your solution can work in any way that involves reflecting syntax and doing most calculation with a Gallina function. These hints outline a particular possible solution. Throughout, the [ring] tactic will be helpful for proving many simple facts about rationals, and tactics like [rewrite] are correctly overloaded to work with rational equality [==]. | 646 Your solution can work in any way that involves reifying syntax and doing most calculation with a Gallina function. These hints outline a particular possible solution. Throughout, the [ring] tactic will be helpful for proving many simple facts about rationals, and tactics like [rewrite] are correctly overloaded to work with rational equality [==]. |
644 | 647 |
645 %\begin{enumerate}%#<ol># | 648 %\begin{enumerate}%#<ol># |
646 %\item%#<li># Define an inductive type [exp] of expressions over rationals (which inhabit the Coq type [Q]). Include variables (represented as natural numbers), constants, addition, subtraction, and multiplication.#</li># | 649 %\item%#<li># Define an inductive type [exp] of expressions over rationals (which inhabit the Coq type [Q]). Include variables (represented as natural numbers), constants, addition, subtraction, and multiplication.#</li># |
647 %\item%#<li># Define a function [lookup] for reading an element out of a list of rationals, by its position in the list.#</li># | 650 %\item%#<li># Define a function [lookup] for reading an element out of a list of rationals, by its position in the list.#</li># |
648 %\item%#<li># Define a function [expDenote] that translates [exp]s, along with lists of rationals representing variable values, to [Q].#</li># | 651 %\item%#<li># Define a function [expDenote] that translates [exp]s, along with lists of rationals representing variable values, to [Q].#</li># |
652 %\item%#<li># Write a recursive function [linearizeEqs : list (exp * Q) -> option (lhs * Q)]. This function linearizes all of the equations in the list in turn, building up the sum of the equations. It returns [None] if the linearization of any constituent equation fails.#</li># | 655 %\item%#<li># Write a recursive function [linearizeEqs : list (exp * Q) -> option (lhs * Q)]. This function linearizes all of the equations in the list in turn, building up the sum of the equations. It returns [None] if the linearization of any constituent equation fails.#</li># |
653 %\item%#<li># Define a denotation function for [lhs].#</li># | 656 %\item%#<li># Define a denotation function for [lhs].#</li># |
654 %\item%#<li># Prove that, when [exp] linearization succeeds on constant [k] and expression [e], the linearized version has the same meaning as [k * e].#</li># | 657 %\item%#<li># Prove that, when [exp] linearization succeeds on constant [k] and expression [e], the linearized version has the same meaning as [k * e].#</li># |
655 %\item%#<li># Prove that, when [linearizeEqs] succeeds on an equation list [eqs], then the final summed-up equation is true whenever the original equation list is true.#</li># | 658 %\item%#<li># Prove that, when [linearizeEqs] succeeds on an equation list [eqs], then the final summed-up equation is true whenever the original equation list is true.#</li># |
656 %\item%#<li># Write a tactic [findVarsHyps] to search through all equalities on rationals in the context, recursing through addition, subtraction, and multiplication to find the list of expressions that should be treated as variables. This list should be suitable as an argument to [expDenote] and [eqsDenote], associating a [Q] value to each natural number that stands for a variable.#</li># | 659 %\item%#<li># Write a tactic [findVarsHyps] to search through all equalities on rationals in the context, recursing through addition, subtraction, and multiplication to find the list of expressions that should be treated as variables. This list should be suitable as an argument to [expDenote] and [eqsDenote], associating a [Q] value to each natural number that stands for a variable.#</li># |
657 %\item%#<li># Write a tactic [reflect] to reflect a [Q] expression into [exp], with respect to a given list of variable values.#</li># | 660 %\item%#<li># Write a tactic [reify] to reify a [Q] expression into [exp], with respect to a given list of variable values.#</li># |
658 %\item%#<li># Write a tactic [reflectEqs] to reflect a formula that begins with a sequence of implications from linear equalities whose lefthand sides are expressed with [expDenote]. This tactic should build a [list (exp * Q)] representing the equations. Remember to give an explicit type annotation when returning a nil list, as in [constr:(@nil (exp * Q))].#</li># | 661 %\item%#<li># Write a tactic [reifyEqs] to reify a formula that begins with a sequence of implications from linear equalities whose lefthand sides are expressed with [expDenote]. This tactic should build a [list (exp * Q)] representing the equations. Remember to give an explicit type annotation when returning a nil list, as in [constr:(][@][nil (exp * Q))].#</li># |
659 %\item%#<li># Now this final tactic should do the job: | 662 %\item%#<li># Now this final tactic should do the job: |
660 | |
661 [[ | 663 [[ |
662 Ltac reflectContext := | 664 Ltac reifyContext := |
663 let ls := findVarsHyps in | 665 let ls := findVarsHyps in |
664 repeat match goal with | 666 repeat match goal with |
665 | [ H : ?e == ?num # ?den |- _ ] => | 667 | [ H : ?e == ?num # ?den |- _ ] => |
666 let r := reflect ls e in | 668 let r := reify ls e in |
667 change (expDenote ls r == num # den) in H; | 669 change (expDenote ls r == num # den) in H; |
668 generalize H | 670 generalize H |
669 end; | 671 end; |
670 match goal with | 672 match goal with |
671 | [ |- ?g ] => let re := reflectEqs g in | 673 | [ |- ?g ] => let re := reifyEqs g in |
672 intros; | 674 intros; |
673 let H := fresh "H" in | 675 let H := fresh "H" in |
674 assert (H : eqsDenote ls re); [ simpl in *; tauto | 676 assert (H : eqsDenote ls re); [ simpl in *; tauto |
675 | repeat match goal with | 677 | repeat match goal with |
676 | [ H : expDenote _ _ == _ |- _ ] => clear H | 678 | [ H : expDenote _ _ == _ |- _ ] => clear H |
679 match goal with | 681 match goal with |
680 | [ |- ?X == ?Y -> _ ] => | 682 | [ |- ?X == ?Y -> _ ] => |
681 ring_simplify X Y; intro | 683 ring_simplify X Y; intro |
682 end ] | 684 end ] |
683 end. | 685 end. |
684 | |
685 ]] | 686 ]] |
686 | 687 |
687 #</ol>#%\end{enumerate}% | 688 #</ol>#%\end{enumerate}% |
688 #</li># | 689 #</li># |
689 | 690 |