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