comparison src/LogicProg.v @ 372:3c039c72eb40

Prose for first LogicProg section
author Adam Chlipala <adam@chlipala.net>
date Mon, 26 Mar 2012 15:35:09 -0400
parents 549d604c3d16
children b13f76abc724
comparison
equal deleted inserted replaced
371:f7c2bf7f1324 372:3c039c72eb40
1 (* Copyright (c) 2011, Adam Chlipala 1 (* Copyright (c) 2011-2012, Adam Chlipala
2 * 2 *
3 * This work is licensed under a 3 * This work is licensed under a
4 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 4 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
5 * Unported License. 5 * Unported License.
6 * The license text is available at: 6 * The license text is available at:
19 19
20 (** %\part{Proof Engineering} 20 (** %\part{Proof Engineering}
21 21
22 \chapter{Proof Search by Logic Programming}% *) 22 \chapter{Proof Search by Logic Programming}% *)
23 23
24 (** Exciting new chapter that is missing prose for the new content! Some content was moved from the next chapter, and it may not seem entirely to fit here yet. *) 24 (** The Curry-Howard correspondence tells us that proving is %``%#"#just#"#%''% programming, but the pragmatics of the two activities are very different. Generally we care about properties of a program besides its type, but the same is not true about proofs. Any proof of a theorem will do just as well. As a result, automated proof search is conceptually simpler than automated programming.
25
26 The paradigm of %\index{logic programming}%logic programming, as embodied in languages like %\index{Prolog}%Prolog, is a good match for proof search in higher-order logic. This chapter introduces the details, attempting to avoid any dependence on past logic programming experience. *)
25 27
26 28
27 (** * Introducing Logic Programming *) 29 (** * Introducing Logic Programming *)
28 30
31 (** Recall the definition of addition from the standard library. *)
32
29 Print plus. 33 Print plus.
34 (** %\vspace{-.15in}%[[
35 plus =
36 fix plus (n m : nat) : nat := match n with
37 | 0 => m
38 | S p => S (plus p m)
39 end
40
41 ]]
42
43 This is a recursive definition, in the style of functional programming. We might also follow the style of logic programming, which corresponds to the inductive relations we have defined in previous chapters. *)
30 44
31 Inductive plusR : nat -> nat -> nat -> Prop := 45 Inductive plusR : nat -> nat -> nat -> Prop :=
32 | PlusO : forall m, plusR O m m 46 | PlusO : forall m, plusR O m m
33 | PlusS : forall n m r, plusR n m r 47 | PlusS : forall n m r, plusR n m r
34 -> plusR (S n) m (S r). 48 -> plusR (S n) m (S r).
35 49
50 (** Intuitively, a fact [plusR n m r] only holds when [plus n m = r]. It is not hard to prove this correspondence formally. *)
51
36 (* begin thide *) 52 (* begin thide *)
37 Hint Constructors plusR. 53 Hint Constructors plusR.
38 (* end thide *) 54 (* end thide *)
39 55
40 Theorem plus_plusR : forall n m, 56 Theorem plus_plusR : forall n m,
50 (* begin thide *) 66 (* begin thide *)
51 induction 1; crush. 67 induction 1; crush.
52 Qed. 68 Qed.
53 (* end thide *) 69 (* end thide *)
54 70
71 (** With the functional definition of [plus], simple equalities about arithmetic follow by computation. *)
72
55 Example four_plus_three : 4 + 3 = 7. 73 Example four_plus_three : 4 + 3 = 7.
56 (* begin thide *) 74 (* begin thide *)
57 reflexivity. 75 reflexivity.
58 Qed. 76 Qed.
59 (* end thide *) 77 (* end thide *)
60 78
79 Print four_plus_three.
80 (** %\vspace{-.15in}%[[
81 four_plus_three = eq_refl
82 ]]
83
84 With the relational definition, the same equalities take more steps to prove, but the process is completely mechanical. For example, consider this simple-minded manual proof search strategy. The steps with error messages shown afterward will be omitted from the final script.
85 *)
86
61 Example four_plus_three' : plusR 4 3 7. 87 Example four_plus_three' : plusR 4 3 7.
62 (* begin thide *) 88 (* begin thide *)
89 (** %\vspace{-.2in}%[[
90 apply PlusO.
91 ]]
92 %\vspace{-.2in}%
93 <<
94 Error: Impossible to unify "plusR 0 ?24 ?24" with "plusR 4 3 7".
95 >> *)
96 apply PlusS.
97 (** %\vspace{-.2in}%[[
98 apply PlusO.
99 ]]
100 %\vspace{-.2in}%
101 <<
102 Error: Impossible to unify "plusR 0 ?25 ?25" with "plusR 3 3 6".
103 >> *)
104 apply PlusS.
105 (** %\vspace{-.2in}%[[
106 apply PlusO.
107 ]]
108 %\vspace{-.2in}%
109 <<
110 Error: Impossible to unify "plusR 0 ?26 ?26" with "plusR 2 3 5".
111 >> *)
112 apply PlusS.
113 (** %\vspace{-.2in}%[[
114 apply PlusO.
115 ]]
116 %\vspace{-.2in}%
117 <<
118 Error: Impossible to unify "plusR 0 ?27 ?27" with "plusR 1 3 4".
119 >> *)
120 apply PlusS.
121 apply PlusO.
122
123 (** At this point the proof is completed. It is no doubt clear that a simple procedure could find all proofs of this kind for us. We are just exploring all possible proof trees, built from the two candidate steps [apply PlusO] and [apply PlusS]. The built-in tactic %\index{tactics!auto}%[auto] does exactly that, since above we used [Hint Constructors] to register the two candidate proof steps as hints. *)
124
125 Restart.
63 auto. 126 auto.
64 Qed. 127 Qed.
65 (* end thide *) 128 (* end thide *)
66 129
67 Example five_plus_three' : plusR 5 3 8. 130 Print four_plus_three'.
68 (* begin thide *) 131 (** %\vspace{-.15in}%[[
132 four_plus_three' = PlusS (PlusS (PlusS (PlusS (PlusO 3))))
133 ]]
134 *)
135
136 (** Let us try the same approach on a slightly more complex goal. *)
137
138 Example five_plus_three : plusR 5 3 8.
139 (* begin thide *)
140 auto.
141
142 (** This time, [auto] is not enough to make any progress. Since even a single candidate step may lead to an infinite space of possible proof trees, [auto] is parameterized on the maximum depth of trees to consider. The default depth is 5, and it turns out that we need depth 6 to prove the goal. *)
143
69 auto 6. 144 auto 6.
145
146 (** Sometimes it is useful to see a description of the proof tree that [auto] finds, with the %\index{tactics!info}%[info] tactical. *)
147
70 Restart. 148 Restart.
71 info auto 6. 149 info auto 6.
72 Qed. 150 (** %\vspace{-.15in}%[[
73 (* end thide *) 151 == apply PlusS; apply PlusS; apply PlusS; apply PlusS;
74 152 apply PlusS; apply PlusO.
75 (* begin thide *) 153 ]]
76 Hint Constructors ex. 154 *)
77 (* end thide *) 155 Qed.
156 (* end thide *)
157
158 (** The two key components of logic programming are %\index{backtracking}\emph{%#<i>#backtracking#</i>#%}% and %\index{unification}\emph{%#<i>#unification#</i>#%}%. To see these techniques in action, consider this further silly example. Here our candidate proof steps will be reflexivity and quantifier instantiation. *)
78 159
79 Example seven_minus_three : exists x, x + 3 = 7. 160 Example seven_minus_three : exists x, x + 3 = 7.
80 (* begin thide *) 161 (* begin thide *)
162 (** For explanatory purposes, let us simulate a user with minimal understanding of arithmetic. We start by choosing an instantiation for the quantifier. Recall that [ex_intro] is the constructor for existentially quantified formulas. *)
163
164 apply ex_intro with 0.
165 (** %\vspace{-.2in}%[[
166 reflexivity.
167 ]]
168 %\vspace{-.2in}%
169 <<
170 Error: Impossible to unify "7" with "0 + 3".
171 >>
172
173 This seems to be a dead end. Let us %\emph{%#<i>#backtrack#</i>#%}% to the point where we ran [apply] and make a better alternate choice.
174 *)
175
176 Restart.
177 apply ex_intro with 4.
178 reflexivity.
179 Qed.
180 (* end thide *)
181
182 (** The above was a fairly tame example of backtracking. In general, any node in an under-construction proof tree may be the destination of backtracking an arbitrarily large number of times, as different candidate proof steps are found not to lead to full proof trees, within the depth bound passed to [auto].
183
184 Next we demonstrate unification, which will be easier when we switch to the relational formulation of addition. *)
185
186 Example seven_minus_three' : exists x, plusR x 3 7.
187 (* begin thide *)
188 (** We could attempt to guess the quantifier instantiation manually as before, but here there is no need. Instead of [apply], we use %\index{tactics!eapply}%[eapply] instead, which proceeds with placeholder %\index{unification variable}\emph{%#<i>#unification variables#</i>#%}% standing in for those parameters we wish to postpone guessing. *)
189
190 eapply ex_intro.
191 (** [[
192 1 subgoal
193
194 ============================
195 plusR ?70 3 7
196 ]]
197
198 Now we can finish the proof with the right applications of [plusR]'s constructors. Note that new unification variables are being generated to stand for new unknowns. *)
199
200 apply PlusS.
201 (** [[
202 ============================
203 plusR ?71 3 6
204 ]]
205 *)
206 apply PlusS. apply PlusS. apply PlusS.
207 (** [[
208 ============================
209 plusR ?74 3 3
210 ]]
211 *)
212 apply PlusO.
213
214 (** The [auto] tactic will not perform these sorts of steps that introduce unification variables, but the %\index{tactics!eauto}%[eauto] tactic will. It is helpful to work with two separate tactics, because proof search in the [eauto] style can uncover many more potential proof trees and hence take much longer to run. *)
215
216 Restart.
217 info eauto 6.
218 (** %\vspace{-.15in}%[[
219 == eapply ex_intro; apply PlusS; apply PlusS;
220 apply PlusS; apply PlusS; apply PlusO.
221 ]]
222 *)
223 Qed.
224 (* end thide *)
225
226 (** This proof gives us our first example where logic programming simplifies proof search compared to functional programming. In general, functional programs are only meant to be run in a single direction; a function has disjoint sets of inputs and outputs. In the last example, we effectively ran a logic program backwards, deducing an input that gives rise to a certain output. The same works for deducing an unknown value of the other input. *)
227
228 Example seven_minus_four' : exists x, plusR 4 x 7.
229 (* begin thide *)
81 eauto 6. 230 eauto 6.
82 Abort. 231 Qed.
83 (* end thide *) 232 (* end thide *)
84 233
85 Example seven_minus_three' : exists x, plusR x 3 7. 234 (** By proving the right auxiliary facts, we can reason about specific functional programs in the same way as we did above for a logic program. Let us prove that the constructors of [plusR] have natural interpretations as lemmas about [plus]. We can find the first such lemma already proved in the standard library, using the %\index{Vernacular commands!SearchRewrite}%[SearchRewrite] command to find a library function proving an equality whose lefthand or righthand side matches a pattern with wildcards. *)
86 (* begin thide *)
87 info eauto 6.
88 Qed.
89 (* end thide *)
90
91 Example seven_minus_four' : exists x, plusR 4 x 7.
92 (* begin thide *)
93 info eauto 6.
94 Qed.
95 (* end thide *)
96 235
97 (* begin thide *) 236 (* begin thide *)
98 SearchRewrite (O + _). 237 SearchRewrite (O + _).
238 (** %\vspace{-.15in}%[[
239 plus_O_n: forall n : nat, 0 + n = n
240 ]]
241
242 The command %\index{Vernacular commands!Hint Immediate}%[Hint Immediate] asks [auto] and [eauto] to consider this lemma as a candidate step for any leaf of a proof tree. *)
99 243
100 Hint Immediate plus_O_n. 244 Hint Immediate plus_O_n.
245
246 (** The counterpart to [PlusS] we will prove ourselves. *)
101 247
102 Lemma plusS : forall n m r, 248 Lemma plusS : forall n m r,
103 n + m = r 249 n + m = r
104 -> S n + m = S r. 250 -> S n + m = S r.
105 crush. 251 crush.
106 Qed. 252 Qed.
107 253
254 (** The command %\index{Vernacular commands!Hint Resolve}%[Hint Resolve] adds a new candidate proof step, to be attempted at any level of a proof tree, not just at leaves. *)
255
108 Hint Resolve plusS. 256 Hint Resolve plusS.
109 (* end thide *) 257 (* end thide *)
110 258
111 Example seven_minus_three : exists x, x + 3 = 7. 259 (** Now that we have registered the proper hints, we can replicate our previous examples with the normal, functional addition [plus]. *)
112 (* begin thide *) 260
113 info eauto 6. 261 Example seven_minus_three'' : exists x, x + 3 = 7.
262 (* begin thide *)
263 eauto 6.
114 Qed. 264 Qed.
115 (* end thide *) 265 (* end thide *)
116 266
117 Example seven_minus_four : exists x, 4 + x = 7. 267 Example seven_minus_four : exists x, 4 + x = 7.
118 (* begin thide *) 268 (* begin thide *)
119 info eauto 6. 269 eauto 6.
120 Qed. 270 Qed.
121 (* end thide *) 271 (* end thide *)
122 272
123 Example hundred_minus_hundred : exists x, 4 + x + 0 = 7. 273 (** This new hint database is far from a complete decision procedure, as we see in a further example that [eauto] does not finish. *)
274
275 Example seven_minus_four_zero : exists x, 4 + x + 0 = 7.
124 (* begin thide *) 276 (* begin thide *)
125 eauto 6. 277 eauto 6.
126 Abort. 278 Abort.
127 (* end thide *) 279 (* end thide *)
280
281 (** A further lemma will be helpful. *)
128 282
129 (* begin thide *) 283 (* begin thide *)
130 Lemma plusO : forall n m, 284 Lemma plusO : forall n m,
131 n = m 285 n = m
132 -> n + 0 = m. 286 -> n + 0 = m.
134 Qed. 288 Qed.
135 289
136 Hint Resolve plusO. 290 Hint Resolve plusO.
137 (* end thide *) 291 (* end thide *)
138 292
293 (** Note that, if we consider the inputs to [plus] as the inputs of a corresponding logic program, the new rule [plusO] introduces an ambiguity. For instance, a sum [0 + 0] would match both of [plus_O_n] and [plusO], depending on which operand we focus on. This ambiguity may increase the number of potential search trees, slowing proof search, but semantically it presents no problems, and in fact it leads to an automated proof of the present example. *)
294
139 Example seven_minus_four_zero : exists x, 4 + x + 0 = 7. 295 Example seven_minus_four_zero : exists x, 4 + x + 0 = 7.
140 (* begin thide *) 296 (* begin thide *)
141 info eauto 7. 297 eauto 7.
142 Qed. 298 Qed.
143 (* end thide *) 299 (* end thide *)
300
301 (** Just how much damage can be done by adding hints that grow the space of possible proof trees? A classic gotcha comes from unrestricted use of transitivity, as embodied in this library theorem about equality: *)
144 302
145 Check eq_trans. 303 Check eq_trans.
304 (** %\vspace{-.15in}%[[
305 eq_trans
306 : forall (A : Type) (x y z : A), x = y -> y = z -> x = z
307 ]]
308 *)
309
310 (** Hints are scoped over sections, so let us enter a section to contain the effects of an unfortunate hint choice. *)
146 311
147 Section slow. 312 Section slow.
148 Hint Resolve eq_trans. 313 Hint Resolve eq_trans.
149 314
315 (** The following fact is false, but that does not stop [eauto] from taking a very long time to search for proofs of it. We use the handy %\index{Vernacular commands!Time}%[Time] command to measure how long a proof step takes to run. None of the following steps make any progress. *)
316
150 Example three_minus_four_zero : exists x, 1 + x = 0. 317 Example three_minus_four_zero : exists x, 1 + x = 0.
151 Time eauto 1. 318 Time eauto 1.
319 (** %\vspace{-.15in}%
320 <<
321 Finished transaction in 0. secs (0.u,0.s)
322 >>
323 *)
324
152 Time eauto 2. 325 Time eauto 2.
326 (** %\vspace{-.15in}%
327 <<
328 Finished transaction in 0. secs (0.u,0.s)
329 >>
330 *)
331
153 Time eauto 3. 332 Time eauto 3.
333 (** %\vspace{-.15in}%
334 <<
335 Finished transaction in 0. secs (0.008u,0.s)
336 >>
337 *)
338
154 Time eauto 4. 339 Time eauto 4.
340 (** %\vspace{-.15in}%
341 <<
342 Finished transaction in 0. secs (0.068005u,0.004s)
343 >>
344 *)
345
155 Time eauto 5. 346 Time eauto 5.
347 (** %\vspace{-.15in}%
348 <<
349 Finished transaction in 2. secs (1.92012u,0.044003s)
350 >>
351 *)
352
353 (** We see worrying exponential growth in running time, and the %\index{tactics!debug}%[debug] tactical helps us see where [eauto] is wasting its time, outputting a trace of every proof step that is attempted. The rule [eq_trans] applies at every node of a proof tree, and [eauto] tries all such positions. *)
354
156 debug eauto 3. 355 debug eauto 3.
356 (** [[
357 1 depth=3
358 1.1 depth=2 eapply ex_intro
359 1.1.1 depth=1 apply plusO
360 1.1.1.1 depth=0 eapply eq_trans
361 1.1.2 depth=1 eapply eq_trans
362 1.1.2.1 depth=1 apply plus_n_O
363 1.1.2.1.1 depth=0 apply plusO
364 1.1.2.1.2 depth=0 eapply eq_trans
365 1.1.2.2 depth=1 apply @eq_refl
366 1.1.2.2.1 depth=0 apply plusO
367 1.1.2.2.2 depth=0 eapply eq_trans
368 1.1.2.3 depth=1 apply eq_add_S ; trivial
369 1.1.2.3.1 depth=0 apply plusO
370 1.1.2.3.2 depth=0 eapply eq_trans
371 1.1.2.4 depth=1 apply eq_sym ; trivial
372 1.1.2.4.1 depth=0 eapply eq_trans
373 1.1.2.5 depth=0 apply plusO
374 1.1.2.6 depth=0 apply plusS
375 1.1.2.7 depth=0 apply f_equal (A:=nat)
376 1.1.2.8 depth=0 apply f_equal2 (A1:=nat) (A2:=nat)
377 1.1.2.9 depth=0 eapply eq_trans
378 ]]
379 *)
157 Abort. 380 Abort.
158 End slow. 381 End slow.
159 382
383 (** Sometimes, though, transitivity is just what is needed to get a proof to go through automatically with [eauto]. For those cases, we can use named %\index{hint databases}\emph{%#<i>#hint databases#</i>#%}% to segragate hints into different groups that may be called on as needed. Here we put [eq_trans] into the database [slow]. *)
384
160 (* begin thide *) 385 (* begin thide *)
161 Hint Resolve eq_trans : slow. 386 Hint Resolve eq_trans : slow.
162 (* end thide *) 387 (* end thide *)
163 388
164 Example three_minus_four_zero : exists x, 1 + x = 0. 389 Example three_minus_four_zero : exists x, 1 + x = 0.
165 (* begin thide *) 390 (* begin thide *)
166 eauto. 391 Time eauto.
392 (** %\vspace{-.15in}%
393 <<
394 Finished transaction in 0. secs (0.004u,0.s)
395 >>
396
397 This [eauto] fails to prove the goal, but it least it takes substantially less than the 2 seconds required above! *)
398
167 Abort. 399 Abort.
168 (* end thide *) 400 (* end thide *)
169 401
402 (** One simple example from before runs in the same amount of time, avoiding pollution by transivity. *)
403
170 Example seven_minus_three_again : exists x, x + 3 = 7. 404 Example seven_minus_three_again : exists x, x + 3 = 7.
171 (* begin thide *) 405 (* begin thide *)
172 eauto 6. 406 Time eauto 6.
173 Qed. 407 (** %\vspace{-.15in}%
174 (* end thide *) 408 <<
409 Finished transaction in 0. secs (0.004001u,0.s)
410 >>
411 %\vspace{-.2in}% *)
412
413 Qed.
414 (* end thide *)
415
416 (** When we %\emph{%#<i>#do#</i>#%}% need transitivity, we ask for it explicitly. *)
175 417
176 Example needs_trans : forall x y, 1 + x = y 418 Example needs_trans : forall x y, 1 + x = y
177 -> y = 2 419 -> y = 2
178 -> exists z, z + x = 3. 420 -> exists z, z + x = 3.
179 (* begin thide *) 421 (* begin thide *)
180 info eauto with slow. 422 info eauto with slow.
181 Qed. 423 (** %\vspace{-.2in}%[[
182 (* end thide *) 424 == intro x; intro y; intro H; intro H0; simple eapply ex_intro;
425 apply plusS; simple eapply eq_trans.
426 exact H.
427
428 exact H0.
429 ]]
430 *)
431 Qed.
432 (* end thide *)
433
434 (** The [info] trace shows that [eq_trans] was used in just the position where it is needed to complete the proof. We also see that [auto] and [eauto] always perform [intro] steps without counting them toward the bound on proof tree depth. *)
183 435
184 436
185 (** * Searching for Underconstrained Values *) 437 (** * Searching for Underconstrained Values *)
186 438
187 Print length. 439 Print length.