Mercurial > cpdt > repo
comparison src/Reflection.v @ 147:f103f28a6b57
my_tauto prose
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Tue, 28 Oct 2008 16:31:55 -0400 |
parents | 2779c651406a |
children | 0952c4ba209b |
comparison
equal
deleted
inserted
replaced
146:2779c651406a | 147:f103f28a6b57 |
---|---|
359 (** Extensions of this basic approach are used in the implementations of the [ring] and [field] tactics that come packaged with Coq. *) | 359 (** Extensions of this basic approach are used in the implementations of the [ring] and [field] tactics that come packaged with Coq. *) |
360 | 360 |
361 | 361 |
362 (** * A Smarter Tautology Solver *) | 362 (** * A Smarter Tautology Solver *) |
363 | 363 |
364 (** 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 cannott 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. | |
365 | |
366 To arrive at a nice implementation satisfying these criteria, we introduce the [quote] tactic and its associated library. *) | |
367 | |
364 Require Import Quote. | 368 Require Import Quote. |
365 | 369 |
366 Inductive formula : Set := | 370 Inductive formula : Set := |
367 | Atomic : index -> formula | 371 | Atomic : index -> formula |
368 | Truth : formula | 372 | Truth : formula |
369 | Falsehood : formula | 373 | Falsehood : formula |
370 | And : formula -> formula -> formula | 374 | And : formula -> formula -> formula |
371 | Or : formula -> formula -> formula | 375 | Or : formula -> formula -> formula |
372 | Imp : formula -> formula -> formula. | 376 | Imp : formula -> formula -> formula. |
373 | 377 |
374 Definition asgn := varmap Prop. | 378 (** 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. |
379 | |
380 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. *) | |
375 | 381 |
376 Definition imp (P1 P2 : Prop) := P1 -> P2. | 382 Definition imp (P1 P2 : Prop) := P1 -> P2. |
377 Infix "-->" := imp (no associativity, at level 95). | 383 Infix "-->" := imp (no associativity, at level 95). |
384 | |
385 (** Now we can define our denotation function. *) | |
386 | |
387 Definition asgn := varmap Prop. | |
378 | 388 |
379 Fixpoint formulaDenote (atomics : asgn) (f : formula) : Prop := | 389 Fixpoint formulaDenote (atomics : asgn) (f : formula) : Prop := |
380 match f with | 390 match f with |
381 | Atomic v => varmap_find False v atomics | 391 | Atomic v => varmap_find False v atomics |
382 | Truth => True | 392 | Truth => True |
384 | And f1 f2 => formulaDenote atomics f1 /\ formulaDenote atomics f2 | 394 | And f1 f2 => formulaDenote atomics f1 /\ formulaDenote atomics f2 |
385 | Or f1 f2 => formulaDenote atomics f1 \/ formulaDenote atomics f2 | 395 | Or f1 f2 => formulaDenote atomics f1 \/ formulaDenote atomics f2 |
386 | Imp f1 f2 => formulaDenote atomics f1 --> formulaDenote atomics f2 | 396 | Imp f1 f2 => formulaDenote atomics f1 --> formulaDenote atomics f2 |
387 end. | 397 end. |
388 | 398 |
399 (** 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. *) | |
400 | |
389 Section my_tauto. | 401 Section my_tauto. |
390 Variable atomics : asgn. | 402 Variable atomics : asgn. |
391 | 403 |
392 Definition holds (v : index) := varmap_find False v atomics. | 404 Definition holds (v : index) := varmap_find False v atomics. |
405 | |
406 (** We define some shorthand for a particular variable being true, and now we are ready to define some helpful functions based on the [ListSet] module of the standard library, which (unsurprisingly) presents a view of lists as sets. *) | |
393 | 407 |
394 Require Import ListSet. | 408 Require Import ListSet. |
395 | 409 |
396 Definition index_eq : forall x y : index, {x = y} + {x <> y}. | 410 Definition index_eq : forall x y : index, {x = y} + {x <> y}. |
397 decide equality. | 411 decide equality. |
398 Defined. | 412 Defined. |
399 | 413 |
400 Definition add (s : set index) (v : index) := set_add index_eq v s. | 414 Definition add (s : set index) (v : index) := set_add index_eq v s. |
415 | |
401 Definition In_dec : forall v (s : set index), {In v s} + {~In v s}. | 416 Definition In_dec : forall v (s : set index), {In v s} + {~In v s}. |
402 Open Local Scope specif_scope. | 417 Open Local Scope specif_scope. |
403 | 418 |
404 intro; refine (fix F (s : set index) : {In v s} + {~In v s} := | 419 intro; refine (fix F (s : set index) : {In v s} + {~In v s} := |
405 match s return {In v s} + {~In v s} with | 420 match s return {In v s} + {~In v s} with |
406 | nil => No | 421 | nil => No |
407 | v' :: s' => index_eq v' v || F s' | 422 | v' :: s' => index_eq v' v || F s' |
408 end); crush. | 423 end); crush. |
409 Defined. | 424 Defined. |
425 | |
426 (** We define what it means for all members of an index set to represent true propositions, and we prove some lemmas about this notion. *) | |
410 | 427 |
411 Fixpoint allTrue (s : set index) : Prop := | 428 Fixpoint allTrue (s : set index) : Prop := |
412 match s with | 429 match s with |
413 | nil => True | 430 | nil => True |
414 | v :: s' => holds v /\ allTrue s' | 431 | v :: s' => holds v /\ allTrue s' |
432 Qed. | 449 Qed. |
433 | 450 |
434 Hint Resolve allTrue_add allTrue_In. | 451 Hint Resolve allTrue_add allTrue_In. |
435 | 452 |
436 Open Local Scope partial_scope. | 453 Open Local Scope partial_scope. |
454 | |
455 (** Now we can write a function [forward] which implements deconstruction of hypotheses. It has a dependent type, in the style of Chapter 6, guaranteeing correctness. The arguments to [forward] are a goal formula [f], a set [known] of atomic formulas that we may assume are true, a hypothesis formula [hyp], and a success continuation [cont] that we call when we have extended [known] to hold new truths implied by [hyp]. *) | |
437 | 456 |
438 Definition forward (f : formula) (known : set index) (hyp : formula) | 457 Definition forward (f : formula) (known : set index) (hyp : formula) |
439 (cont : forall known', [allTrue known' -> formulaDenote atomics f]) | 458 (cont : forall known', [allTrue known' -> formulaDenote atomics f]) |
440 : [allTrue known -> formulaDenote atomics hyp -> formulaDenote atomics f]. | 459 : [allTrue known -> formulaDenote atomics hyp -> formulaDenote atomics f]. |
441 refine (fix F (f : formula) (known : set index) (hyp : formula) | 460 refine (fix F (f : formula) (known : set index) (hyp : formula) |
451 | Or h1 h2 => F f known h1 cont && F f known h2 cont | 470 | Or h1 h2 => F f known h1 cont && F f known h2 cont |
452 | Imp _ _ => Reduce (cont known) | 471 | Imp _ _ => Reduce (cont known) |
453 end); crush. | 472 end); crush. |
454 Defined. | 473 Defined. |
455 | 474 |
475 (** A [backward] function implements analysis of the final goal. It calls [forward] to handle implications. *) | |
476 | |
456 Definition backward (known : set index) (f : formula) : [allTrue known -> formulaDenote atomics f]. | 477 Definition backward (known : set index) (f : formula) : [allTrue known -> formulaDenote atomics f]. |
457 refine (fix F (known : set index) (f : formula) : [allTrue known -> formulaDenote atomics f] := | 478 refine (fix F (known : set index) (f : formula) : [allTrue known -> formulaDenote atomics f] := |
458 match f return [allTrue known -> formulaDenote atomics f] with | 479 match f return [allTrue known -> formulaDenote atomics f] with |
459 | Atomic v => Reduce (In_dec v known) | 480 | Atomic v => Reduce (In_dec v known) |
460 | Truth => Yes | 481 | Truth => Yes |
463 | Or f1 f2 => F known f1 || F known f2 | 484 | Or f1 f2 => F known f1 || F known f2 |
464 | Imp f1 f2 => forward f2 known f1 (fun known' => F known' f2) | 485 | Imp f1 f2 => forward f2 known f1 (fun known' => F known' f2) |
465 end); crush; eauto. | 486 end); crush; eauto. |
466 Defined. | 487 Defined. |
467 | 488 |
489 (** A simple wrapper around [backward] gives us the usual type of a partial decision procedure. *) | |
490 | |
468 Definition my_tauto (f : formula) : [formulaDenote atomics f]. | 491 Definition my_tauto (f : formula) : [formulaDenote atomics f]. |
469 intro; refine (Reduce (backward nil f)); crush. | 492 intro; refine (Reduce (backward nil f)); crush. |
470 Defined. | 493 Defined. |
471 End my_tauto. | 494 End my_tauto. |
495 | |
496 (** 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. *) | |
472 | 497 |
473 Ltac my_tauto := | 498 Ltac my_tauto := |
474 repeat match goal with | 499 repeat match goal with |
475 | [ |- forall x : ?P, _ ] => | 500 | [ |- forall x : ?P, _ ] => |
476 match type of P with | 501 match type of P with |
481 quote formulaDenote; | 506 quote formulaDenote; |
482 match goal with | 507 match goal with |
483 | [ |- formulaDenote ?m ?f ] => exact (partialOut (my_tauto m f)) | 508 | [ |- formulaDenote ?m ?f ] => exact (partialOut (my_tauto m f)) |
484 end. | 509 end. |
485 | 510 |
511 (** A few examples demonstrate how the tactic works. *) | |
512 | |
486 Theorem mt1 : True. | 513 Theorem mt1 : True. |
487 my_tauto. | 514 my_tauto. |
488 Qed. | 515 Qed. |
489 | 516 |
490 Print mt1. | 517 Print mt1. |
518 (** [[ | |
519 | |
520 mt1 = partialOut (my_tauto (Empty_vm Prop) Truth) | |
521 : True | |
522 ]] | |
523 | |
524 We see [my_tauto] applied with an empty [varmap], since every subformula is handled by [formulaDenote]. *) | |
491 | 525 |
492 Theorem mt2 : forall x y : nat, x = y --> x = y. | 526 Theorem mt2 : forall x y : nat, x = y --> x = y. |
493 my_tauto. | 527 my_tauto. |
494 Qed. | 528 Qed. |
495 | 529 |
496 Print mt2. | 530 Print mt2. |
531 (** [[ | |
532 | |
533 mt2 = | |
534 fun x y : nat => | |
535 partialOut | |
536 (my_tauto (Node_vm (x = y) (Empty_vm Prop) (Empty_vm Prop)) | |
537 (Imp (Atomic End_idx) (Atomic End_idx))) | |
538 : forall x y : nat, x = y --> x = y | |
539 ]] | |
540 | |
541 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. *) | |
497 | 542 |
498 Theorem mt3 : forall x y z, | 543 Theorem mt3 : forall x y z, |
499 (x < y /\ y > z) \/ (y > z /\ x < S y) | 544 (x < y /\ y > z) \/ (y > z /\ x < S y) |
500 --> y > z /\ (x < y \/ x < S y). | 545 --> y > z /\ (x < y \/ x < S y). |
501 my_tauto. | 546 my_tauto. |
502 Qed. | 547 Qed. |
503 | 548 |
504 Print mt3. | 549 Print mt3. |
550 (** [[ | |
551 | |
552 fun x y z : nat => | |
553 partialOut | |
554 (my_tauto | |
555 (Node_vm (x < S y) (Node_vm (x < y) (Empty_vm Prop) (Empty_vm Prop)) | |
556 (Node_vm (y > z) (Empty_vm Prop) (Empty_vm Prop))) | |
557 (Imp | |
558 (Or (And (Atomic (Left_idx End_idx)) (Atomic (Right_idx End_idx))) | |
559 (And (Atomic (Right_idx End_idx)) (Atomic End_idx))) | |
560 (And (Atomic (Right_idx End_idx)) | |
561 (Or (Atomic (Left_idx End_idx)) (Atomic End_idx))))) | |
562 : forall x y z : nat, | |
563 x < y /\ y > z \/ y > z /\ x < S y --> y > z /\ (x < y \/ x < S y) | |
564 ]] | |
565 | |
566 Our goal contained three distinct atomic formulas, and we see that a three-element [varmap] is generated. | |
567 | |
568 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. *) | |
505 | 569 |
506 Theorem mt4 : True /\ True /\ True /\ True /\ True /\ True /\ False --> False. | 570 Theorem mt4 : True /\ True /\ True /\ True /\ True /\ True /\ False --> False. |
507 my_tauto. | 571 my_tauto. |
508 Qed. | 572 Qed. |
509 | 573 |
510 Print mt4. | 574 Print mt4. |
575 (** [[ | |
576 | |
577 mt4 = | |
578 partialOut | |
579 (my_tauto (Empty_vm Prop) | |
580 (Imp | |
581 (And Truth | |
582 (And Truth | |
583 (And Truth (And Truth (And Truth (And Truth Falsehood)))))) | |
584 Falsehood)) | |
585 : True /\ True /\ True /\ True /\ True /\ True /\ False --> False | |
586 ]] *) | |
511 | 587 |
512 Theorem mt4' : True /\ True /\ True /\ True /\ True /\ True /\ False -> False. | 588 Theorem mt4' : True /\ True /\ True /\ True /\ True /\ True /\ False -> False. |
513 tauto. | 589 tauto. |
514 Qed. | 590 Qed. |
515 | 591 |
516 Print mt4'. | 592 Print mt4'. |
593 (** [[ | |
594 | |
595 mt4' = | |
596 fun H : True /\ True /\ True /\ True /\ True /\ True /\ False => | |
597 and_ind | |
598 (fun (_ : True) (H1 : True /\ True /\ True /\ True /\ True /\ False) => | |
599 and_ind | |
600 (fun (_ : True) (H3 : True /\ True /\ True /\ True /\ False) => | |
601 and_ind | |
602 (fun (_ : True) (H5 : True /\ True /\ True /\ False) => | |
603 and_ind | |
604 (fun (_ : True) (H7 : True /\ True /\ False) => | |
605 and_ind | |
606 (fun (_ : True) (H9 : True /\ False) => | |
607 and_ind (fun (_ : True) (H11 : False) => False_ind False H11) | |
608 H9) H7) H5) H3) H1) H | |
609 : True /\ True /\ True /\ True /\ True /\ True /\ False -> False | |
610 ]] *) | |
611 |