adam@324: (* Copyright (c) 2011, Adam Chlipala
adam@324: *
adam@324: * This work is licensed under a
adam@324: * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
adam@324: * Unported License.
adam@324: * The license text is available at:
adam@324: * http://creativecommons.org/licenses/by-nc-nd/3.0/
adam@324: *)
adam@324:
adam@324: (* begin hide *)
adam@324:
adam@324: Require Import List.
adam@324:
adam@324: Require Import CpdtTactics.
adam@324:
adam@324: Set Implicit Arguments.
adam@324:
adam@324: (* end hide *)
adam@324:
adam@324: (** %\part{Proof Engineering}
adam@324:
adam@324: \chapter{Proof Search by Logic Programming}% *)
adam@324:
adam@324: (** 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. *)
adam@324:
adam@324:
adam@324: (** * Introducing Logic Programming *)
adam@324:
adam@324: Print plus.
adam@324:
adam@324: Inductive plusR : nat -> nat -> nat -> Prop :=
adam@324: | PlusO : forall m, plusR O m m
adam@324: | PlusS : forall n m r, plusR n m r
adam@324: -> plusR (S n) m (S r).
adam@324:
adam@324: (* begin thide *)
adam@324: Hint Constructors plusR.
adam@324: (* end thide *)
adam@324:
adam@324: Theorem plus_plusR : forall n m,
adam@324: plusR n m (n + m).
adam@324: (* begin thide *)
adam@324: induction n; crush.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Theorem plusR_plus : forall n m r,
adam@324: plusR n m r
adam@324: -> r = n + m.
adam@324: (* begin thide *)
adam@324: induction 1; crush.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Example four_plus_three : 4 + 3 = 7.
adam@324: (* begin thide *)
adam@324: reflexivity.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Example four_plus_three' : plusR 4 3 7.
adam@324: (* begin thide *)
adam@324: auto.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Example five_plus_three' : plusR 5 3 8.
adam@324: (* begin thide *)
adam@324: auto 6.
adam@324: Restart.
adam@324: info auto 6.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: (* begin thide *)
adam@324: Hint Constructors ex.
adam@324: (* end thide *)
adam@324:
adam@324: Example seven_minus_three : exists x, x + 3 = 7.
adam@324: (* begin thide *)
adam@324: eauto 6.
adam@324: Abort.
adam@324: (* end thide *)
adam@324:
adam@324: Example seven_minus_three' : exists x, plusR x 3 7.
adam@324: (* begin thide *)
adam@324: info eauto 6.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Example seven_minus_four' : exists x, plusR 4 x 7.
adam@324: (* begin thide *)
adam@324: info eauto 6.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: (* begin thide *)
adam@324: SearchRewrite (O + _).
adam@324:
adam@324: Hint Immediate plus_O_n.
adam@324:
adam@324: Lemma plusS : forall n m r,
adam@324: n + m = r
adam@324: -> S n + m = S r.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve plusS.
adam@324: (* end thide *)
adam@324:
adam@324: Example seven_minus_three : exists x, x + 3 = 7.
adam@324: (* begin thide *)
adam@324: info eauto 6.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Example seven_minus_four : exists x, 4 + x = 7.
adam@324: (* begin thide *)
adam@324: info eauto 6.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Example hundred_minus_hundred : exists x, 4 + x + 0 = 7.
adam@324: (* begin thide *)
adam@324: eauto 6.
adam@324: Abort.
adam@324: (* end thide *)
adam@324:
adam@324: (* begin thide *)
adam@324: Lemma plusO : forall n m,
adam@324: n = m
adam@324: -> n + 0 = m.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve plusO.
adam@324: (* end thide *)
adam@324:
adam@324: Example seven_minus_four_zero : exists x, 4 + x + 0 = 7.
adam@324: (* begin thide *)
adam@324: info eauto 7.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Check eq_trans.
adam@324:
adam@324: Section slow.
adam@324: Hint Resolve eq_trans.
adam@324:
adam@324: Example three_minus_four_zero : exists x, 1 + x = 0.
adam@324: Time eauto 1.
adam@324: Time eauto 2.
adam@324: Time eauto 3.
adam@324: Time eauto 4.
adam@324: Time eauto 5.
adam@324: debug eauto 3.
adam@324: Abort.
adam@324: End slow.
adam@324:
adam@324: (* begin thide *)
adam@324: Hint Resolve eq_trans : slow.
adam@324: (* end thide *)
adam@324:
adam@324: Example three_minus_four_zero : exists x, 1 + x = 0.
adam@324: (* begin thide *)
adam@324: eauto.
adam@324: Abort.
adam@324: (* end thide *)
adam@324:
adam@324: Example seven_minus_three_again : exists x, x + 3 = 7.
adam@324: (* begin thide *)
adam@324: eauto 6.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Example needs_trans : forall x y, 1 + x = y
adam@324: -> y = 2
adam@324: -> exists z, z + x = 3.
adam@324: (* begin thide *)
adam@324: info eauto with slow.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324:
adam@324: (** * Searching for Underconstrained Values *)
adam@324:
adam@324: Print length.
adam@324:
adam@324: Example length_1_2 : length (1 :: 2 :: nil) = 2.
adam@324: auto.
adam@324: Qed.
adam@324:
adam@324: Print length_1_2.
adam@324:
adam@324: (* begin thide *)
adam@324: Theorem length_O : forall A, length (nil (A := A)) = O.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Theorem length_S : forall A (h : A) t n,
adam@324: length t = n
adam@324: -> length (h :: t) = S n.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve length_O length_S.
adam@324: (* end thide *)
adam@324:
adam@324: Example length_is_2 : exists ls : list nat, length ls = 2.
adam@324: (* begin thide *)
adam@324: eauto.
adam@324:
adam@324: Show Proof.
adam@324: Abort.
adam@324: (* end thide *)
adam@324:
adam@324: Print Forall.
adam@324:
adam@324: Example length_is_2 : exists ls : list nat, length ls = 2
adam@324: /\ Forall (fun n => n >= 1) ls.
adam@324: (* begin thide *)
adam@324: eauto 9.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Definition sum := fold_right plus O.
adam@324:
adam@324: (* begin thide *)
adam@324: Lemma plusO' : forall n m,
adam@324: n = m
adam@324: -> 0 + n = m.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve plusO'.
adam@324:
adam@324: Hint Extern 1 (sum _ = _) => simpl.
adam@324: (* end thide *)
adam@324:
adam@324: Example length_and_sum : exists ls : list nat, length ls = 2
adam@324: /\ sum ls = O.
adam@324: (* begin thide *)
adam@324: eauto 7.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Print length_and_sum.
adam@324:
adam@324: Example length_and_sum' : exists ls : list nat, length ls = 5
adam@324: /\ sum ls = 42.
adam@324: (* begin thide *)
adam@324: eauto 15.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Print length_and_sum'.
adam@324:
adam@324: Example length_and_sum'' : exists ls : list nat, length ls = 2
adam@324: /\ sum ls = 3
adam@324: /\ Forall (fun n => n <> 0) ls.
adam@324: (* begin thide *)
adam@324: eauto 11.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Print length_and_sum''.
adam@324:
adam@324:
adam@324: (** * Synthesizing Programs *)
adam@324:
adam@324: Inductive exp : Set :=
adam@324: | Const : nat -> exp
adam@324: | Var : exp
adam@324: | Plus : exp -> exp -> exp.
adam@324:
adam@324: Inductive eval (var : nat) : exp -> nat -> Prop :=
adam@324: | EvalConst : forall n, eval var (Const n) n
adam@324: | EvalVar : eval var Var var
adam@324: | EvalPlus : forall e1 e2 n1 n2, eval var e1 n1
adam@324: -> eval var e2 n2
adam@324: -> eval var (Plus e1 e2) (n1 + n2).
adam@324:
adam@324: (* begin thide *)
adam@324: Hint Constructors eval.
adam@324: (* end thide *)
adam@324:
adam@324: Example eval1 : forall var, eval var (Plus Var (Plus (Const 8) Var)) (var + (8 + var)).
adam@324: (* begin thide *)
adam@324: auto.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Example eval1' : forall var, eval var (Plus Var (Plus (Const 8) Var)) (2 * var + 8).
adam@324: (* begin thide *)
adam@324: eauto.
adam@324: Abort.
adam@324: (* end thide *)
adam@324:
adam@324: (* begin thide *)
adam@324: Theorem EvalPlus' : forall var e1 e2 n1 n2 n, eval var e1 n1
adam@324: -> eval var e2 n2
adam@324: -> n1 + n2 = n
adam@324: -> eval var (Plus e1 e2) n.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve EvalPlus'.
adam@324:
adam@324: Hint Extern 1 (_ = _) => abstract omega.
adam@324: (* end thide *)
adam@324:
adam@324: Example eval1' : forall var, eval var (Plus Var (Plus (Const 8) Var)) (2 * var + 8).
adam@324: (* begin thide *)
adam@324: eauto.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Print eval1'.
adam@324:
adam@324: Example synthesize1 : exists e, forall var, eval var e (var + 7).
adam@324: (* begin thide *)
adam@324: eauto.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Print synthesize1.
adam@324:
adam@324: Example synthesize2 : exists e, forall var, eval var e (2 * var + 8).
adam@324: (* begin thide *)
adam@324: eauto.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Print synthesize2.
adam@324:
adam@324: Example synthesize3 : exists e, forall var, eval var e (3 * var + 42).
adam@324: (* begin thide *)
adam@324: eauto.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: Print synthesize3.
adam@324:
adam@324: (* begin thide *)
adam@324: Theorem EvalConst' : forall var n m, n = m
adam@324: -> eval var (Const n) m.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve EvalConst'.
adam@324:
adam@324: Theorem zero_times : forall n m r,
adam@324: r = m
adam@324: -> r = 0 * n + m.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve zero_times.
adam@324:
adam@324: Theorem EvalVar' : forall var n,
adam@324: var = n
adam@324: -> eval var Var n.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve EvalVar'.
adam@324:
adam@324: Theorem plus_0 : forall n r,
adam@324: r = n
adam@324: -> r = n + 0.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Theorem times_1 : forall n, n = 1 * n.
adam@324: crush.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve plus_0 times_1.
adam@324:
adam@324: Require Import Arith Ring.
adam@324:
adam@324: Theorem combine : forall x k1 k2 n1 n2,
adam@324: (k1 * x + n1) + (k2 * x + n2) = (k1 + k2) * x + (n1 + n2).
adam@324: intros; ring.
adam@324: Qed.
adam@324:
adam@324: Hint Resolve combine.
adam@324:
adam@324: Theorem linear : forall e, exists k, exists n,
adam@324: forall var, eval var e (k * var + n).
adam@324: induction e; crush; eauto.
adam@324: Qed.
adam@324:
adam@324: Print linear.
adam@324: (* end thide *)
adam@324:
adam@324:
adam@324: (** * More on [auto] Hints *)
adam@324:
adam@324: (** Another class of built-in tactics includes [auto], [eauto], and [autorewrite]. These are based on %\textit{%##hint databases##%}%, which we have seen extended in many examples so far. These tactics are important, because, in Ltac programming, we cannot create %``%#"#global variables#"#%''% whose values can be extended seamlessly by different modules in different source files. We have seen the advantages of hints so far, where [crush] can be defined once and for all, while still automatically applying the hints we add throughout developments.
adam@324:
adam@324: The basic hints for [auto] and [eauto] are [Hint Immediate lemma], asking to try solving a goal immediately by applying a lemma and discharging any hypotheses with a single proof step each; [Resolve lemma], which does the same but may add new premises that are themselves to be subjects of nested proof search; [Constructors type], which acts like [Resolve] applied to every constructor of an inductive type; and [Unfold ident], which tries unfolding [ident] when it appears at the head of a proof goal. Each of these [Hint] commands may be used with a suffix, as in [Hint Resolve lemma : my_db]. This adds the hint only to the specified database, so that it would only be used by, for instance, [auto with my_db]. An additional argument to [auto] specifies the maximum depth of proof trees to search in depth-first order, as in [auto 8] or [auto 8 with my_db]. The default depth is 5.
adam@324:
adam@324: All of these [Hint] commands can be issued alternatively with a more primitive hint kind, [Extern]. A few examples should do best to explain how [Hint Extern] works. *)
adam@324:
adam@324: Theorem bool_neq : true <> false.
adam@324: (* begin thide *)
adam@324: auto.
adam@324:
adam@324: (** [crush] would have discharged this goal, but the default hint database for [auto] contains no hint that applies. *)
adam@324:
adam@324: Abort.
adam@324:
adam@324: (** It is hard to come up with a [bool]-specific hint that is not just a restatement of the theorem we mean to prove. Luckily, a simpler form suffices. *)
adam@324:
adam@324: Hint Extern 1 (_ <> _) => congruence.
adam@324:
adam@324: Theorem bool_neq : true <> false.
adam@324: auto.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: (** Our hint says: %``%#"#whenever the conclusion matches the pattern [_ <> _], try applying [congruence].#"#%''% The [1] is a cost for this rule. During proof search, whenever multiple rules apply, rules are tried in increasing cost order, so it pays to assign high costs to relatively expensive [Extern] hints.
adam@324:
adam@324: [Extern] hints may be implemented with the full Ltac language. This example shows a case where a hint uses a [match]. *)
adam@324:
adam@324: Section forall_and.
adam@324: Variable A : Set.
adam@324: Variables P Q : A -> Prop.
adam@324:
adam@324: Hypothesis both : forall x, P x /\ Q x.
adam@324:
adam@324: Theorem forall_and : forall z, P z.
adam@324: (* begin thide *)
adam@324: crush.
adam@324:
adam@324: (** [crush] makes no progress beyond what [intros] would have accomplished. [auto] will not apply the hypothesis [both] to prove the goal, because the conclusion of [both] does not unify with the conclusion of the goal. However, we can teach [auto] to handle this kind of goal. *)
adam@324:
adam@324: Hint Extern 1 (P ?X) =>
adam@324: match goal with
adam@324: | [ H : forall x, P x /\ _ |- _ ] => apply (proj1 (H X))
adam@324: end.
adam@324:
adam@324: auto.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: (** We see that an [Extern] pattern may bind unification variables that we use in the associated tactic. [proj1] is a function from the standard library for extracting a proof of [R] from a proof of [R /\ S]. *)
adam@324:
adam@324: End forall_and.
adam@324:
adam@324: (** After our success on this example, we might get more ambitious and seek to generalize the hint to all possible predicates [P].
adam@324:
adam@324: [[
adam@324: Hint Extern 1 (?P ?X) =>
adam@324: match goal with
adam@324: | [ H : forall x, P x /\ _ |- _ ] => apply (proj1 (H X))
adam@324: end.
adam@324:
adam@324: User error: Bound head variable
adam@324:
adam@324: ]]
adam@324:
adam@324: Coq's [auto] hint databases work as tables mapping %\textit{%##head symbols##%}% to lists of tactics to try. Because of this, the constant head of an [Extern] pattern must be determinable statically. In our first [Extern] hint, the head symbol was [not], since [x <> y] desugars to [not (eq x y)]; and, in the second example, the head symbol was [P].
adam@324:
adam@324: This restriction on [Extern] hints is the main limitation of the [auto] mechanism, preventing us from using it for general context simplifications that are not keyed off of the form of the conclusion. This is perhaps just as well, since we can often code more efficient tactics with specialized Ltac programs, and we will see how in the next chapter. *)
adam@324:
adam@324:
adam@324: (** * Rewrite Hints *)
adam@324:
adam@324: (** We have used [Hint Rewrite] in many examples so far. [crush] uses these hints by calling [autorewrite]. Our rewrite hints have taken the form [Hint Rewrite lemma : cpdt], adding them to the [cpdt] rewrite database. This is because, in contrast to [auto], [autorewrite] has no default database. Thus, we set the convention that [crush] uses the [cpdt] database.
adam@324:
adam@324: This example shows a direct use of [autorewrite]. *)
adam@324:
adam@324: Section autorewrite.
adam@324: Variable A : Set.
adam@324: Variable f : A -> A.
adam@324:
adam@324: Hypothesis f_f : forall x, f (f x) = f x.
adam@324:
adam@324: Hint Rewrite f_f : my_db.
adam@324:
adam@324: Lemma f_f_f : forall x, f (f (f x)) = f x.
adam@324: intros; autorewrite with my_db; reflexivity.
adam@324: Qed.
adam@324:
adam@324: (** There are a few ways in which [autorewrite] can lead to trouble when insufficient care is taken in choosing hints. First, the set of hints may define a nonterminating rewrite system, in which case invocations to [autorewrite] may not terminate. Second, we may add hints that %``%#"#lead [autorewrite] down the wrong path.#"#%''% For instance: *)
adam@324:
adam@324: Section garden_path.
adam@324: Variable g : A -> A.
adam@324: Hypothesis f_g : forall x, f x = g x.
adam@324: Hint Rewrite f_g : my_db.
adam@324:
adam@324: Lemma f_f_f' : forall x, f (f (f x)) = f x.
adam@324: intros; autorewrite with my_db.
adam@324: (** [[
adam@324: ============================
adam@324: g (g (g x)) = g x
adam@324: ]]
adam@324: *)
adam@324:
adam@324: Abort.
adam@324:
adam@324: (** Our new hint was used to rewrite the goal into a form where the old hint could no longer be applied. This %``%#"#non-monotonicity#"#%''% of rewrite hints contrasts with the situation for [auto], where new hints may slow down proof search but can never %``%#"#break#"#%''% old proofs. The key difference is that [auto] either solves a goal or makes no changes to it, while [autorewrite] may change goals without solving them. The situation for [eauto] is slightly more complicated, as changes to hint databases may change the proof found for a particular goal, and that proof may influence the settings of unification variables that appear elsewhere in the proof state. *)
adam@324:
adam@324: Reset garden_path.
adam@324:
adam@324: (** [autorewrite] also works with quantified equalities that include additional premises, but we must be careful to avoid similar incorrect rewritings. *)
adam@324:
adam@324: Section garden_path.
adam@324: Variable P : A -> Prop.
adam@324: Variable g : A -> A.
adam@324: Hypothesis f_g : forall x, P x -> f x = g x.
adam@324: Hint Rewrite f_g : my_db.
adam@324:
adam@324: Lemma f_f_f' : forall x, f (f (f x)) = f x.
adam@324: intros; autorewrite with my_db.
adam@324: (** [[
adam@324:
adam@324: ============================
adam@324: g (g (g x)) = g x
adam@324:
adam@324: subgoal 2 is:
adam@324: P x
adam@324: subgoal 3 is:
adam@324: P (f x)
adam@324: subgoal 4 is:
adam@324: P (f x)
adam@324: ]]
adam@324: *)
adam@324:
adam@324: Abort.
adam@324:
adam@324: (** The inappropriate rule fired the same three times as before, even though we know we will not be able to prove the premises. *)
adam@324:
adam@324: Reset garden_path.
adam@324:
adam@324: (** Our final, successful, attempt uses an extra argument to [Hint Rewrite] that specifies a tactic to apply to generated premises. Such a hint is only used when the tactic succeeds for all premises, possibly leaving further subgoals for some premises. *)
adam@324:
adam@324: Section garden_path.
adam@324: Variable P : A -> Prop.
adam@324: Variable g : A -> A.
adam@324: Hypothesis f_g : forall x, P x -> f x = g x.
adam@324: (* begin thide *)
adam@324: Hint Rewrite f_g using assumption : my_db.
adam@324: (* end thide *)
adam@324:
adam@324: Lemma f_f_f' : forall x, f (f (f x)) = f x.
adam@324: (* begin thide *)
adam@324: intros; autorewrite with my_db; reflexivity.
adam@324: Qed.
adam@324: (* end thide *)
adam@324:
adam@324: (** [autorewrite] will still use [f_g] when the generated premise is among our assumptions. *)
adam@324:
adam@324: Lemma f_f_f_g : forall x, P x -> f (f x) = g x.
adam@324: (* begin thide *)
adam@324: intros; autorewrite with my_db; reflexivity.
adam@324: (* end thide *)
adam@324: Qed.
adam@324: End garden_path.
adam@324:
adam@324: (** remove printing * *)
adam@324:
adam@324: (** It can also be useful to use the [autorewrite with db in *] form, which does rewriting in hypotheses, as well as in the conclusion. *)
adam@324:
adam@324: (** printing * $*$ *)
adam@324:
adam@324: Lemma in_star : forall x y, f (f (f (f x))) = f (f y)
adam@324: -> f x = f (f (f y)).
adam@324: (* begin thide *)
adam@324: intros; autorewrite with my_db in *; assumption.
adam@324: (* end thide *)
adam@324: Qed.
adam@324:
adam@324: End autorewrite.