diff --git a/src/Doc/Prog_Prove/Logic.thy b/src/Doc/Prog_Prove/Logic.thy --- a/src/Doc/Prog_Prove/Logic.thy +++ b/src/Doc/Prog_Prove/Logic.thy @@ -1,878 +1,880 @@ (*<*) theory Logic imports LaTeXsugar begin (*>*) text\ \vspace{-5ex} \section{Formulas} The core syntax of formulas (\textit{form} below) provides the standard logical constructs, in decreasing order of precedence: \[ \begin{array}{rcl} \mathit{form} & ::= & \(form)\ ~\mid~ \<^const>\True\ ~\mid~ \<^const>\False\ ~\mid~ \<^prop>\term = term\\\ &\mid& \<^prop>\\ form\\index{$HOL4@\isasymnot} ~\mid~ \<^prop>\form \ form\\index{$HOL0@\isasymand} ~\mid~ \<^prop>\form \ form\\index{$HOL1@\isasymor} ~\mid~ \<^prop>\form \ form\\index{$HOL2@\isasymlongrightarrow}\\ &\mid& \<^prop>\\x. form\\index{$HOL6@\isasymforall} ~\mid~ \<^prop>\\x. form\\index{$HOL7@\isasymexists} \end{array} \] Terms are the ones we have seen all along, built from constants, variables, function application and \\\-abstraction, including all the syntactic sugar like infix symbols, \if\, \case\, etc. \begin{warn} Remember that formulas are simply terms of type \bool\. Hence \=\ also works for formulas. Beware that \=\ has a higher precedence than the other logical operators. Hence \<^prop>\s = t \ A\ means \(s = t) \ A\, and \<^prop>\A\B = B\A\ means \A \ (B = B) \ A\. Logical equivalence can also be written with \\\ instead of \=\, where \\\ has the same low precedence as \\\. Hence \A \ B \ B \ A\ really means \(A \ B) \ (B \ A)\. \end{warn} \begin{warn} Quantifiers need to be enclosed in parentheses if they are nested within other constructs (just like \if\, \case\ and \let\). \end{warn} The most frequent logical symbols and their ASCII representations are shown in Fig.~\ref{fig:log-symbols}. \begin{figure} \begin{center} \begin{tabular}{l@ {\qquad}l@ {\qquad}l} \\\ & \xsymbol{forall} & \texttt{ALL}\\ \\\ & \xsymbol{exists} & \texttt{EX}\\ \\\ & \xsymbol{lambda} & \texttt{\%}\\ \\\ & \texttt{-{\kern0pt}->}\\ \\\ & \texttt{<->}\\ \\\ & \texttt{/\char`\\} & \texttt{\&}\\ \\\ & \texttt{\char`\\/} & \texttt{|}\\ \\\ & \xsymbol{not} & \texttt{\char`~}\\ \\\ & \xsymbol{noteq} & \texttt{\char`~=} \end{tabular} \end{center} \caption{Logical symbols and their ASCII forms} \label{fig:log-symbols} \end{figure} The first column shows the symbols, the other columns ASCII representations. The \texttt{\char`\\}\texttt{<...>} form is always converted into the symbolic form by the Isabelle interfaces, the treatment of the other ASCII forms depends on the interface. The ASCII forms \texttt{/\char`\\} and \texttt{\char`\\/} are special in that they are merely keyboard shortcuts for the interface and not logical symbols by themselves. \begin{warn} The implication \\\ is part of the Isabelle framework. It structures theorems and proof states, separating assumptions from conclusions. The implication \\\ is part of the logic HOL and can occur inside the formulas that make up the assumptions and conclusion. Theorems should be of the form \\ A\<^sub>1; \; A\<^sub>n \ \ A\, not \A\<^sub>1 \ \ \ A\<^sub>n \ A\. Both are logically equivalent but the first one works better when using the theorem in further proofs. + +The ASCII representation of \\\ and \\\ is \texttt{[|} and \texttt{|]}. \end{warn} \section{Sets} \label{sec:Sets} Sets of elements of type \<^typ>\'a\ have type \<^typ>\'a set\\index{set@\set\}. They can be finite or infinite. Sets come with the usual notation: \begin{itemize} \item \indexed{\<^term>\{}\}{$IMP042},\quad \{e\<^sub>1,\,e\<^sub>n}\ \item \<^prop>\e \ A\\index{$HOLSet0@\isasymin},\quad \<^prop>\A \ B\\index{$HOLSet2@\isasymsubseteq} \item \<^term>\A \ B\\index{$HOLSet4@\isasymunion},\quad \<^term>\A \ B\\index{$HOLSet5@\isasyminter},\quad \<^term>\A - B\,\quad \<^term>\-A\ \end{itemize} (where \<^term>\A-B\ and \-A\ are set difference and complement) and much more. \<^const>\UNIV\ is the set of all elements of some type. Set comprehension\index{set comprehension} is written \<^term>\{x. P}\\index{$IMP042@\<^term>\{x. P}\} rather than \{x | P}\. \begin{warn} In \<^term>\{x. P}\ the \x\ must be a variable. Set comprehension involving a proper term \t\ must be written \noquotes{@{term[source] "{t | x y. P}"}}\index{$IMP042@\{t |x. P}\}, where \x y\ are those free variables in \t\ that occur in \P\. This is just a shorthand for \<^term>\{v. \x y. v = t \ P}\, where \v\ is a new variable. For example, \<^term>\{x+y|x. x \ A}\ is short for \noquotes{@{term[source]"{v. \x. v = x+y \ x \ A}"}}. \end{warn} Here are the ASCII representations of the mathematical symbols: \begin{center} \begin{tabular}{l@ {\quad}l@ {\quad}l} \\\ & \texttt{\char`\\\char`\} & \texttt{:}\\ \\\ & \texttt{\char`\\\char`\} & \texttt{<=}\\ \\\ & \texttt{\char`\\\char`\} & \texttt{Un}\\ \\\ & \texttt{\char`\\\char`\} & \texttt{Int} \end{tabular} \end{center} Sets also allow bounded quantifications \<^prop>\\x \ A. P\ and \<^prop>\\x \ A. P\. For the more ambitious, there are also \\\\index{$HOLSet6@\isasymUnion} and \\\\index{$HOLSet7@\isasymInter}: \begin{center} @{thm Union_eq} \qquad @{thm Inter_eq} \end{center} The ASCII forms of \\\ are \texttt{\char`\\\char`\} and \texttt{Union}, those of \\\ are \texttt{\char`\\\char`\} and \texttt{Inter}. There are also indexed unions and intersections: \begin{center} @{thm[eta_contract=false] UNION_eq} \\ @{thm[eta_contract=false] INTER_eq} \end{center} The ASCII forms are \ \texttt{UN x:A.~B} \ and \ \texttt{INT x:A. B} \ where \texttt{x} may occur in \texttt{B}. If \texttt{A} is \texttt{UNIV} you can write \ \texttt{UN x.~B} \ and \ \texttt{INT x. B}. Some other frequently useful functions on sets are the following: \begin{center} \begin{tabular}{l@ {\quad}l} @{const_typ set}\index{set@\<^const>\set\} & converts a list to the set of its elements\\ @{const_typ finite}\index{finite@\<^const>\finite\} & is true iff its argument is finite\\ \noquotes{@{term[source] "card :: 'a set \ nat"}}\index{card@\<^const>\card\} & is the cardinality of a finite set\\ & and is \0\ for all infinite sets\\ @{thm image_def}\index{$IMP042@\<^term>\f ` A\} & is the image of a function over a set \end{tabular} \end{center} See @{cite "Nipkow-Main"} for the wealth of further predefined functions in theory \<^theory>\Main\. \subsection*{Exercises} \exercise Start from the data type of binary trees defined earlier: \ datatype 'a tree = Tip | Node "'a tree" 'a "'a tree" text\ Define a function \set ::\ \<^typ>\'a tree \ 'a set\ that returns the elements in a tree and a function \ord ::\ \<^typ>\int tree \ bool\ that tests if an \<^typ>\int tree\ is ordered. Define a function \ins\ that inserts an element into an ordered \<^typ>\int tree\ while maintaining the order of the tree. If the element is already in the tree, the same tree should be returned. Prove correctness of \ins\: \<^prop>\set(ins x t) = {x} \ set t\ and \<^prop>\ord t \ ord(ins i t)\. \endexercise \section{Proof Automation} So far we have only seen \simp\ and \indexed{\auto\}{auto}: Both perform rewriting, both can also prove linear arithmetic facts (no multiplication), and \auto\ is also able to prove simple logical or set-theoretic goals: \ lemma "\x. \y. x = y" by auto lemma "A \ B \ C \ A \ B \ C" by auto text\where \begin{quote} \isacom{by} \textit{proof-method} \end{quote} is short for \begin{quote} \isacom{apply} \textit{proof-method}\\ \isacom{done} \end{quote} The key characteristics of both \simp\ and \auto\ are \begin{itemize} \item They show you where they got stuck, giving you an idea how to continue. \item They perform the obvious steps but are highly incomplete. \end{itemize} A proof method is \conceptnoidx{complete} if it can prove all true formulas. There is no complete proof method for HOL, not even in theory. Hence all our proof methods only differ in how incomplete they are. A proof method that is still incomplete but tries harder than \auto\ is \indexed{\fastforce\}{fastforce}. It either succeeds or fails, it acts on the first subgoal only, and it can be modified like \auto\, e.g., with \simp add\. Here is a typical example of what \fastforce\ can do: \ lemma "\ \xs \ A. \ys. xs = ys @ ys; us \ A \ \ \n. length us = n+n" by fastforce text\This lemma is out of reach for \auto\ because of the quantifiers. Even \fastforce\ fails when the quantifier structure becomes more complicated. In a few cases, its slow version \force\ succeeds where \fastforce\ fails. The method of choice for complex logical goals is \indexed{\blast\}{blast}. In the following example, \T\ and \A\ are two binary predicates. It is shown that if \T\ is total, \A\ is antisymmetric and \T\ is a subset of \A\, then \A\ is a subset of \T\: \ lemma "\ \x y. T x y \ T y x; \x y. A x y \ A y x \ x = y; \x y. T x y \ A x y \ \ \x y. A x y \ T x y" by blast text\ We leave it to the reader to figure out why this lemma is true. Method \blast\ \begin{itemize} \item is (in principle) a complete proof procedure for first-order formulas, a fragment of HOL. In practice there is a search bound. \item does no rewriting and knows very little about equality. \item covers logic, sets and relations. \item either succeeds or fails. \end{itemize} Because of its strength in logic and sets and its weakness in equality reasoning, it complements the earlier proof methods. \subsection{\concept{Sledgehammer}} Command \isacom{sledgehammer} calls a number of external automatic theorem provers (ATPs) that run for up to 30 seconds searching for a proof. Some of these ATPs are part of the Isabelle installation, others are queried over the internet. If successful, a proof command is generated and can be inserted into your proof. The biggest win of \isacom{sledgehammer} is that it will take into account the whole lemma library and you do not need to feed in any lemma explicitly. For example,\ lemma "\ xs @ ys = ys @ xs; length xs = length ys \ \ xs = ys" txt\cannot be solved by any of the standard proof methods, but \isacom{sledgehammer} finds the following proof:\ by (metis append_eq_conv_conj) text\We do not explain how the proof was found but what this command means. For a start, Isabelle does not trust external tools (and in particular not the translations from Isabelle's logic to those tools!) and insists on a proof that it can check. This is what \indexed{\metis\}{metis} does. It is given a list of lemmas and tries to find a proof using just those lemmas (and pure logic). In contrast to using \simp\ and friends who know a lot of lemmas already, using \metis\ manually is tedious because one has to find all the relevant lemmas first. But that is precisely what \isacom{sledgehammer} does for us. In this case lemma @{thm[source]append_eq_conv_conj} alone suffices: @{thm[display] append_eq_conv_conj} We leave it to the reader to figure out why this lemma suffices to prove the above lemma, even without any knowledge of what the functions \<^const>\take\ and \<^const>\drop\ do. Keep in mind that the variables in the two lemmas are independent of each other, despite the same names, and that you can substitute arbitrary values for the free variables in a lemma. Just as for the other proof methods we have seen, there is no guarantee that \isacom{sledgehammer} will find a proof if it exists. Nor is \isacom{sledgehammer} superior to the other proof methods. They are incomparable. Therefore it is recommended to apply \simp\ or \auto\ before invoking \isacom{sledgehammer} on what is left. \subsection{Arithmetic} By arithmetic formulas we mean formulas involving variables, numbers, \+\, \-\, \=\, \<\, \\\ and the usual logical connectives \\\, \\\, \\\, \\\, \\\. Strictly speaking, this is known as \concept{linear arithmetic} because it does not involve multiplication, although multiplication with numbers, e.g., \2*n\, is allowed. Such formulas can be proved by \indexed{\arith\}{arith}: \ lemma "\ (a::nat) \ x + b; 2*x < c \ \ 2*a + 1 \ 2*b + c" by arith text\In fact, \auto\ and \simp\ can prove many linear arithmetic formulas already, like the one above, by calling a weak but fast version of \arith\. Hence it is usually not necessary to invoke \arith\ explicitly. The above example involves natural numbers, but integers (type \<^typ>\int\) and real numbers (type \real\) are supported as well. As are a number of further operators like \<^const>\min\ and \<^const>\max\. On \<^typ>\nat\ and \<^typ>\int\, \arith\ can even prove theorems with quantifiers in them, but we will not enlarge on that here. \subsection{Trying Them All} If you want to try all of the above automatic proof methods you simply type \begin{isabelle} \isacom{try} \end{isabelle} There is also a lightweight variant \isacom{try0} that does not call sledgehammer. If desired, specific simplification and introduction rules can be added: \begin{isabelle} \isacom{try0} \simp: \ intro: \\ \end{isabelle} \section{Single Step Proofs} Although automation is nice, it often fails, at least initially, and you need to find out why. When \fastforce\ or \blast\ simply fail, you have no clue why. At this point, the stepwise application of proof rules may be necessary. For example, if \blast\ fails on \<^prop>\A \ B\, you want to attack the two conjuncts \A\ and \B\ separately. This can be achieved by applying \emph{conjunction introduction} \[ @{thm[mode=Rule,show_question_marks]conjI}\ \conjI\ \] to the proof state. We will now examine the details of this process. \subsection{Instantiating Unknowns} We had briefly mentioned earlier that after proving some theorem, Isabelle replaces all free variables \x\ by so called \conceptidx{unknowns}{unknown} \?x\. We can see this clearly in rule @{thm[source] conjI}. These unknowns can later be instantiated explicitly or implicitly: \begin{itemize} \item By hand, using \indexed{\of\}{of}. The expression \conjI[of "a=b" "False"]\ instantiates the unknowns in @{thm[source] conjI} from left to right with the two formulas \a=b\ and \False\, yielding the rule @{thm[display,mode=Rule,margin=100]conjI[of "a=b" False]} In general, \th[of string\<^sub>1 \ string\<^sub>n]\ instantiates the unknowns in the theorem \th\ from left to right with the terms \string\<^sub>1\ to \string\<^sub>n\. \item By unification. \conceptidx{Unification}{unification} is the process of making two terms syntactically equal by suitable instantiations of unknowns. For example, unifying \?P \ ?Q\ with \mbox{\<^prop>\a=b \ False\} instantiates \?P\ with \<^prop>\a=b\ and \?Q\ with \<^prop>\False\. \end{itemize} We need not instantiate all unknowns. If we want to skip a particular one we can write \_\ instead, for example \conjI[of _ "False"]\. Unknowns can also be instantiated by name using \indexed{\where\}{where}, for example \conjI[where ?P = "a=b"\ \isacom{and} \?Q = "False"]\. \subsection{Rule Application} \conceptidx{Rule application}{rule application} means applying a rule backwards to a proof state. For example, applying rule @{thm[source]conjI} to a proof state \begin{quote} \1. \ \ A \ B\ \end{quote} results in two subgoals, one for each premise of @{thm[source]conjI}: \begin{quote} \1. \ \ A\\\ \2. \ \ B\ \end{quote} In general, the application of a rule \\ A\<^sub>1; \; A\<^sub>n \ \ A\ to a subgoal \mbox{\\ \ C\} proceeds in two steps: \begin{enumerate} \item Unify \A\ and \C\, thus instantiating the unknowns in the rule. \item Replace the subgoal \C\ with \n\ new subgoals \A\<^sub>1\ to \A\<^sub>n\. \end{enumerate} This is the command to apply rule \xyz\: \begin{quote} \isacom{apply}\(rule xyz)\\index{rule@\rule\} \end{quote} This is also called \concept{backchaining} with rule \xyz\. \subsection{Introduction Rules} Conjunction introduction (@{thm[source] conjI}) is one example of a whole class of rules known as \conceptidx{introduction rules}{introduction rule}. They explain under which premises some logical construct can be introduced. Here are some further useful introduction rules: \[ \inferrule*[right=\mbox{\impI\}]{\mbox{\?P \ ?Q\}}{\mbox{\?P \ ?Q\}} \qquad \inferrule*[right=\mbox{\allI\}]{\mbox{\\x. ?P x\}}{\mbox{\\x. ?P x\}} \] \[ \inferrule*[right=\mbox{\iffI\}]{\mbox{\?P \ ?Q\} \\ \mbox{\?Q \ ?P\}} {\mbox{\?P = ?Q\}} \] These rules are part of the logical system of \concept{natural deduction} (e.g., @{cite HuthRyan}). Although we intentionally de-emphasize the basic rules of logic in favour of automatic proof methods that allow you to take bigger steps, these rules are helpful in locating where and why automation fails. When applied backwards, these rules decompose the goal: \begin{itemize} \item @{thm[source] conjI} and @{thm[source]iffI} split the goal into two subgoals, \item @{thm[source] impI} moves the left-hand side of a HOL implication into the list of assumptions, \item and @{thm[source] allI} removes a \\\ by turning the quantified variable into a fixed local variable of the subgoal. \end{itemize} Isabelle knows about these and a number of other introduction rules. The command \begin{quote} \isacom{apply} \rule\\index{rule@\rule\} \end{quote} automatically selects the appropriate rule for the current subgoal. You can also turn your own theorems into introduction rules by giving them the \indexed{\intro\}{intro} attribute, analogous to the \simp\ attribute. In that case \blast\, \fastforce\ and (to a limited extent) \auto\ will automatically backchain with those theorems. The \intro\ attribute should be used with care because it increases the search space and can lead to nontermination. Sometimes it is better to use it only in specific calls of \blast\ and friends. For example, @{thm[source] le_trans}, transitivity of \\\ on type \<^typ>\nat\, is not an introduction rule by default because of the disastrous effect on the search space, but can be useful in specific situations: \ lemma "\ (a::nat) \ b; b \ c; c \ d; d \ e \ \ a \ e" by(blast intro: le_trans) text\ Of course this is just an example and could be proved by \arith\, too. \subsection{Forward Proof} \label{sec:forward-proof} Forward proof means deriving new theorems from old theorems. We have already seen a very simple form of forward proof: the \of\ operator for instantiating unknowns in a theorem. The big brother of \of\ is \indexed{\OF\}{OF} for applying one theorem to others. Given a theorem \<^prop>\A \ B\ called \r\ and a theorem \A'\ called \r'\, the theorem \r[OF r']\ is the result of applying \r\ to \r'\, where \r\ should be viewed as a function taking a theorem \A\ and returning \B\. More precisely, \A\ and \A'\ are unified, thus instantiating the unknowns in \B\, and the result is the instantiated \B\. Of course, unification may also fail. \begin{warn} Application of rules to other rules operates in the forward direction: from the premises to the conclusion of the rule; application of rules to proof states operates in the backward direction, from the conclusion to the premises. \end{warn} In general \r\ can be of the form \\ A\<^sub>1; \; A\<^sub>n \ \ A\ and there can be multiple argument theorems \r\<^sub>1\ to \r\<^sub>m\ (with \m \ n\), in which case \r[OF r\<^sub>1 \ r\<^sub>m]\ is obtained by unifying and thus proving \A\<^sub>i\ with \r\<^sub>i\, \i = 1\m\. Here is an example, where @{thm[source]refl} is the theorem @{thm[show_question_marks] refl}: \ thm conjI[OF refl[of "a"] refl[of "b"]] text\yields the theorem @{thm conjI[OF refl[of "a"] refl[of "b"]]}. The command \isacom{thm} merely displays the result. Forward reasoning also makes sense in connection with proof states. Therefore \blast\, \fastforce\ and \auto\ support a modifier \dest\ which instructs the proof method to use certain rules in a forward fashion. If \r\ is of the form \mbox{\A \ B\}, the modifier \mbox{\dest: r\}\index{dest@\dest:\} allows proof search to reason forward with \r\, i.e., to replace an assumption \A'\, where \A'\ unifies with \A\, with the correspondingly instantiated \B\. For example, @{thm[source,show_question_marks] Suc_leD} is the theorem \mbox{@{thm Suc_leD}}, which works well for forward reasoning: \ lemma "Suc(Suc(Suc a)) \ b \ a \ b" by(blast dest: Suc_leD) text\In this particular example we could have backchained with @{thm[source] Suc_leD}, too, but because the premise is more complicated than the conclusion this can easily lead to nontermination. %\subsection{Finding Theorems} % %Command \isacom{find{\isacharunderscorekeyword}theorems} searches for specific theorems in the current %theory. Search criteria include pattern matching on terms and on names. %For details see the Isabelle/Isar Reference Manual~@{cite IsarRef}. %\bigskip \begin{warn} To ease readability we will drop the question marks in front of unknowns from now on. \end{warn} \section{Inductive Definitions} \label{sec:inductive-defs}\index{inductive definition|(} Inductive definitions are the third important definition facility, after datatypes and recursive function. \ifsem In fact, they are the key construct in the definition of operational semantics in the second part of the book. \fi \subsection{An Example: Even Numbers} \label{sec:Logic:even} Here is a simple example of an inductively defined predicate: \begin{itemize} \item 0 is even \item If $n$ is even, so is $n+2$. \end{itemize} The operative word ``inductive'' means that these are the only even numbers. In Isabelle we give the two rules the names \ev0\ and \evSS\ and write \ inductive ev :: "nat \ bool" where ev0: "ev 0" | evSS: (*<*)"ev n \ ev (Suc(Suc n))"(*>*) text_raw\@{prop[source]"ev n \ ev (n + 2)"}\ text\To get used to inductive definitions, we will first prove a few properties of \<^const>\ev\ informally before we descend to the Isabelle level. How do we prove that some number is even, e.g., \<^prop>\ev 4\? Simply by combining the defining rules for \<^const>\ev\: \begin{quote} \ev 0 \ ev (0 + 2) \ ev((0 + 2) + 2) = ev 4\ \end{quote} \subsubsection{Rule Induction}\index{rule induction|(} Showing that all even numbers have some property is more complicated. For example, let us prove that the inductive definition of even numbers agrees with the following recursive one:\ fun evn :: "nat \ bool" where "evn 0 = True" | "evn (Suc 0) = False" | "evn (Suc(Suc n)) = evn n" text\We prove \<^prop>\ev m \ evn m\. That is, we assume \<^prop>\ev m\ and by induction on the form of its derivation prove \<^prop>\evn m\. There are two cases corresponding to the two rules for \<^const>\ev\: \begin{description} \item[Case @{thm[source]ev0}:] \<^prop>\ev m\ was derived by rule \<^prop>\ev 0\: \\ \\\ \<^prop>\m=(0::nat)\ \\\ \evn m = evn 0 = True\ \item[Case @{thm[source]evSS}:] \<^prop>\ev m\ was derived by rule \<^prop>\ev n \ ev(n+2)\: \\ \\\ \<^prop>\m=n+(2::nat)\ and by induction hypothesis \<^prop>\evn n\\\ \\\ \evn m = evn(n + 2) = evn n = True\ \end{description} What we have just seen is a special case of \concept{rule induction}. Rule induction applies to propositions of this form \begin{quote} \<^prop>\ev n \ P n\ \end{quote} That is, we want to prove a property \<^prop>\P n\ for all even \n\. But if we assume \<^prop>\ev n\, then there must be some derivation of this assumption using the two defining rules for \<^const>\ev\. That is, we must prove \begin{description} \item[Case @{thm[source]ev0}:] \<^prop>\P(0::nat)\ \item[Case @{thm[source]evSS}:] \<^prop>\\ ev n; P n \ \ P(n + 2::nat)\ \end{description} The corresponding rule is called @{thm[source] ev.induct} and looks like this: \[ \inferrule{ \mbox{@{thm (prem 1) ev.induct[of "n"]}}\\ \mbox{@{thm (prem 2) ev.induct}}\\ \mbox{\<^prop>\!!n. \ ev n; P n \ \ P(n+2)\}} {\mbox{@{thm (concl) ev.induct[of "n"]}}} \] The first premise \<^prop>\ev n\ enforces that this rule can only be applied in situations where we know that \n\ is even. Note that in the induction step we may not just assume \<^prop>\P n\ but also \mbox{\<^prop>\ev n\}, which is simply the premise of rule @{thm[source] evSS}. Here is an example where the local assumption \<^prop>\ev n\ comes in handy: we prove \<^prop>\ev m \ ev(m - 2)\ by induction on \<^prop>\ev m\. Case @{thm[source]ev0} requires us to prove \<^prop>\ev(0 - 2)\, which follows from \<^prop>\ev 0\ because \<^prop>\0 - 2 = (0::nat)\ on type \<^typ>\nat\. In case @{thm[source]evSS} we have \mbox{\<^prop>\m = n+(2::nat)\} and may assume \<^prop>\ev n\, which implies \<^prop>\ev (m - 2)\ because \m - 2 = (n + 2) - 2 = n\. We did not need the induction hypothesis at all for this proof (it is just a case analysis of which rule was used) but having \<^prop>\ev n\ at our disposal in case @{thm[source]evSS} was essential. This case analysis of rules is also called ``rule inversion'' and is discussed in more detail in \autoref{ch:Isar}. \subsubsection{In Isabelle} Let us now recast the above informal proofs in Isabelle. For a start, we use \<^const>\Suc\ terms instead of numerals in rule @{thm[source]evSS}: @{thm[display] evSS} This avoids the difficulty of unifying \n+2\ with some numeral, which is not automatic. The simplest way to prove \<^prop>\ev(Suc(Suc(Suc(Suc 0))))\ is in a forward direction: \evSS[OF evSS[OF ev0]]\ yields the theorem @{thm evSS[OF evSS[OF ev0]]}. Alternatively, you can also prove it as a lemma in backwards fashion. Although this is more verbose, it allows us to demonstrate how each rule application changes the proof state:\ lemma "ev(Suc(Suc(Suc(Suc 0))))" txt\ @{subgoals[display,indent=0,goals_limit=1]} \ apply(rule evSS) txt\ @{subgoals[display,indent=0,goals_limit=1]} \ apply(rule evSS) txt\ @{subgoals[display,indent=0,goals_limit=1]} \ apply(rule ev0) done text\\indent Rule induction is applied by giving the induction rule explicitly via the \rule:\ modifier:\index{inductionrule@\induction ... rule:\}\ lemma "ev m \ evn m" apply(induction rule: ev.induct) by(simp_all) text\Both cases are automatic. Note that if there are multiple assumptions of the form \<^prop>\ev t\, method \induction\ will induct on the leftmost one. As a bonus, we also prove the remaining direction of the equivalence of \<^const>\ev\ and \<^const>\evn\: \ lemma "evn n \ ev n" apply(induction n rule: evn.induct) txt\This is a proof by computation induction on \n\ (see \autoref{sec:recursive-funs}) that sets up three subgoals corresponding to the three equations for \<^const>\evn\: @{subgoals[display,indent=0]} The first and third subgoals follow with @{thm[source]ev0} and @{thm[source]evSS}, and the second subgoal is trivially true because \<^prop>\evn(Suc 0)\ is \<^const>\False\: \ by (simp_all add: ev0 evSS) text\The rules for \<^const>\ev\ make perfect simplification and introduction rules because their premises are always smaller than the conclusion. It makes sense to turn them into simplification and introduction rules permanently, to enhance proof automation. They are named @{thm[source] ev.intros} \index{intros@\.intros\} by Isabelle:\ declare ev.intros[simp,intro] text\The rules of an inductive definition are not simplification rules by default because, in contrast to recursive functions, there is no termination requirement for inductive definitions. \subsubsection{Inductive Versus Recursive} We have seen two definitions of the notion of evenness, an inductive and a recursive one. Which one is better? Much of the time, the recursive one is more convenient: it allows us to do rewriting in the middle of terms, and it expresses both the positive information (which numbers are even) and the negative information (which numbers are not even) directly. An inductive definition only expresses the positive information directly. The negative information, for example, that \1\ is not even, has to be proved from it (by induction or rule inversion). On the other hand, rule induction is tailor-made for proving \mbox{\<^prop>\ev n \ P n\} because it only asks you to prove the positive cases. In the proof of \<^prop>\evn n \ P n\ by computation induction via @{thm[source]evn.induct}, we are also presented with the trivial negative cases. If you want the convenience of both rewriting and rule induction, you can make two definitions and show their equivalence (as above) or make one definition and prove additional properties from it, for example rule induction from computation induction. But many concepts do not admit a recursive definition at all because there is no datatype for the recursion (for example, the transitive closure of a relation), or the recursion would not terminate (for example, an interpreter for a programming language). Even if there is a recursive definition, if we are only interested in the positive information, the inductive definition may be much simpler. \subsection{The Reflexive Transitive Closure} \label{sec:star} Evenness is really more conveniently expressed recursively than inductively. As a second and very typical example of an inductive definition we define the reflexive transitive closure. \ifsem It will also be an important building block for some of the semantics considered in the second part of the book. \fi The reflexive transitive closure, called \star\ below, is a function that maps a binary predicate to another binary predicate: if \r\ is of type \\ \ \ \ bool\ then \<^term>\star r\ is again of type \\ \ \ \ bool\, and \<^prop>\star r x y\ means that \x\ and \y\ are in the relation \<^term>\star r\. Think \<^term>\r\<^sup>*\ when you see \<^term>\star r\, because \star r\ is meant to be the reflexive transitive closure. That is, \<^prop>\star r x y\ is meant to be true if from \x\ we can reach \y\ in finitely many \r\ steps. This concept is naturally defined inductively:\ inductive star :: "('a \ 'a \ bool) \ 'a \ 'a \ bool" for r where refl: "star r x x" | step: "r x y \ star r y z \ star r x z" text\The base case @{thm[source] refl} is reflexivity: \<^term>\x=y\. The step case @{thm[source]step} combines an \r\ step (from \x\ to \y\) and a \<^term>\star r\ step (from \y\ to \z\) into a \<^term>\star r\ step (from \x\ to \z\). The ``\isacom{for}~\r\'' in the header is merely a hint to Isabelle that \r\ is a fixed parameter of \<^const>\star\, in contrast to the further parameters of \<^const>\star\, which change. As a result, Isabelle generates a simpler induction rule. By definition \<^term>\star r\ is reflexive. It is also transitive, but we need rule induction to prove that:\ lemma star_trans: "star r x y \ star r y z \ star r x z" apply(induction rule: star.induct) (*<*) defer apply(rename_tac u x y) defer (*>*) txt\The induction is over \<^prop>\star r x y\ (the first matching assumption) and we try to prove \mbox{\<^prop>\star r y z \ star r x z\}, which we abbreviate by \<^prop>\P x y\. These are our two subgoals: @{subgoals[display,indent=0]} The first one is \<^prop>\P x x\, the result of case @{thm[source]refl}, and it is trivial:\index{assumption@\assumption\} \ apply(assumption) txt\Let us examine subgoal \2\, case @{thm[source] step}. Assumptions \<^prop>\r u x\ and \mbox{\<^prop>\star r x y\} are the premises of rule @{thm[source]step}. Assumption \<^prop>\star r y z \ star r x z\ is \mbox{\<^prop>\P x y\}, the IH coming from \<^prop>\star r x y\. We have to prove \<^prop>\P u y\, which we do by assuming \<^prop>\star r y z\ and proving \<^prop>\star r u z\. The proof itself is straightforward: from \mbox{\<^prop>\star r y z\} the IH leads to \<^prop>\star r x z\ which, together with \<^prop>\r u x\, leads to \mbox{\<^prop>\star r u z\} via rule @{thm[source]step}: \ apply(metis step) done text\\index{rule induction|)} \subsection{The General Case} Inductive definitions have approximately the following general form: \begin{quote} \isacom{inductive} \I :: "\ \ bool"\ \isacom{where} \end{quote} followed by a sequence of (possibly named) rules of the form \begin{quote} \\ I a\<^sub>1; \; I a\<^sub>n \ \ I a\ \end{quote} separated by \|\. As usual, \n\ can be 0. The corresponding rule induction principle \I.induct\ applies to propositions of the form \begin{quote} \<^prop>\I x \ P x\ \end{quote} where \P\ may itself be a chain of implications. \begin{warn} Rule induction is always on the leftmost premise of the goal. Hence \I x\ must be the first premise. \end{warn} Proving \<^prop>\I x \ P x\ by rule induction means proving for every rule of \I\ that \P\ is invariant: \begin{quote} \\ I a\<^sub>1; P a\<^sub>1; \; I a\<^sub>n; P a\<^sub>n \ \ P a\ \end{quote} The above format for inductive definitions is simplified in a number of respects. \I\ can have any number of arguments and each rule can have additional premises not involving \I\, so-called \conceptidx{side conditions}{side condition}. In rule inductions, these side conditions appear as additional assumptions. The \isacom{for} clause seen in the definition of the reflexive transitive closure simplifies the induction rule. \index{inductive definition|)} \subsection*{Exercises} \begin{exercise} Formalize the following definition of palindromes \begin{itemize} \item The empty list and a singleton list are palindromes. \item If \xs\ is a palindrome, so is \<^term>\a # xs @ [a]\. \end{itemize} as an inductive predicate \palindrome ::\ \<^typ>\'a list \ bool\ and prove that \<^prop>\rev xs = xs\ if \xs\ is a palindrome. \end{exercise} \exercise We could also have defined \<^const>\star\ as follows: \ inductive star' :: "('a \ 'a \ bool) \ 'a \ 'a \ bool" for r where refl': "star' r x x" | step': "star' r x y \ r y z \ star' r x z" text\ The single \r\ step is performed after rather than before the \star'\ steps. Prove \<^prop>\star' r x y \ star r x y\ and \<^prop>\star r x y \ star' r x y\. You may need lemmas. Note that rule induction fails if the assumption about the inductive predicate is not the first assumption. \endexercise \begin{exercise}\label{exe:iter} Analogous to \<^const>\star\, give an inductive definition of the \n\-fold iteration of a relation \r\: \<^term>\iter r n x y\ should hold if there are \x\<^sub>0\, \dots, \x\<^sub>n\ such that \<^prop>\x = x\<^sub>0\, \<^prop>\x\<^sub>n = y\ and \r x\<^bsub>i\<^esub> x\<^bsub>i+1\<^esub>\ for all \<^prop>\i < n\. Correct and prove the following claim: \<^prop>\star r x y \ iter r n x y\. \end{exercise} \begin{exercise}\label{exe:cfg} A context-free grammar can be seen as an inductive definition where each nonterminal $A$ is an inductively defined predicate on lists of terminal symbols: $A(w)$ means that $w$ is in the language generated by $A$. For example, the production $S \to a S b$ can be viewed as the implication \<^prop>\S w \ S (a # w @ [b])\ where \a\ and \b\ are terminal symbols, i.e., elements of some alphabet. The alphabet can be defined like this: \isacom{datatype} \alpha = a | b | \\ Define the two grammars (where $\varepsilon$ is the empty word) \[ \begin{array}{r@ {\quad}c@ {\quad}l} S &\to& \varepsilon \quad\mid\quad aSb \quad\mid\quad SS \\ T &\to& \varepsilon \quad\mid\quad TaTb \end{array} \] as two inductive predicates. If you think of \a\ and \b\ as ``\(\'' and ``\)\'', the grammar defines strings of balanced parentheses. Prove \<^prop>\T w \ S w\ and \mbox{\<^prop>\S w \ T w\} separately and conclude \<^prop>\S w = T w\. \end{exercise} \ifsem \begin{exercise} In \autoref{sec:AExp} we defined a recursive evaluation function \aval :: aexp \ state \ val\. Define an inductive evaluation predicate \aval_rel :: aexp \ state \ val \ bool\ and prove that it agrees with the recursive function: \<^prop>\aval_rel a s v \ aval a s = v\, \<^prop>\aval a s = v \ aval_rel a s v\ and thus \noquotes{@{prop [source] "aval_rel a s v \ aval a s = v"}}. \end{exercise} \begin{exercise} Consider the stack machine from Chapter~3 and recall the concept of \concept{stack underflow} from Exercise~\ref{exe:stack-underflow}. Define an inductive predicate \ok :: nat \ instr list \ nat \ bool\ such that \ok n is n'\ means that with any initial stack of length \n\ the instructions \is\ can be executed without stack underflow and that the final stack has length \n'\. Prove that \ok\ correctly computes the final stack size @{prop[display] "\ok n is n'; length stk = n\ \ length (exec is s stk) = n'"} and that instruction sequences generated by \comp\ cannot cause stack underflow: \ \ok n (comp a) ?\ \ for some suitable value of \?\. \end{exercise} \fi \ (*<*) end (*>*) diff --git a/src/Doc/Prog_Prove/Types_and_funs.thy b/src/Doc/Prog_Prove/Types_and_funs.thy --- a/src/Doc/Prog_Prove/Types_and_funs.thy +++ b/src/Doc/Prog_Prove/Types_and_funs.thy @@ -1,604 +1,604 @@ (*<*) theory Types_and_funs imports Main begin (*>*) text\ \vspace{-5ex} \section{Type and Function Definitions} Type synonyms are abbreviations for existing types, for example \index{string@\string\}\ type_synonym string = "char list" text\ Type synonyms are expanded after parsing and are not present in internal representation and output. They are mere conveniences for the reader. \subsection{Datatypes} \label{sec:datatypes} The general form of a datatype definition looks like this: \begin{quote} \begin{tabular}{@ {}rclcll} \indexed{\isacom{datatype}}{datatype} \('a\<^sub>1,\,'a\<^sub>n)t\ & = & $C_1 \ \"\\tau_{1,1}\"\ \dots \"\\tau_{1,n_1}\"\$ \\ & $|$ & \dots \\ & $|$ & $C_k \ \"\\tau_{k,1}\"\ \dots \"\\tau_{k,n_k}\"\$ \end{tabular} \end{quote} It introduces the constructors \ $C_i :: \tau_{i,1}\Rightarrow \cdots \Rightarrow \tau_{i,n_i} \Rightarrow$~\('a\<^sub>1,\,'a\<^sub>n)t\ \ and expresses that any value of this type is built from these constructors in a unique manner. Uniqueness is implied by the following properties of the constructors: \begin{itemize} \item \emph{Distinctness:} $C_i\ \ldots \neq C_j\ \dots$ \quad if $i \neq j$ \item \emph{Injectivity:} \begin{tabular}[t]{l} $(C_i \ x_1 \dots x_{n_i} = C_i \ y_1 \dots y_{n_i}) =$\\ $(x_1 = y_1 \land \dots \land x_{n_i} = y_{n_i})$ \end{tabular} \end{itemize} The fact that any value of the datatype is built from the constructors implies the \concept{structural induction}\index{induction} rule: to show $P~x$ for all $x$ of type \('a\<^sub>1,\,'a\<^sub>n)t\, one needs to show $P(C_i\ x_1 \dots x_{n_i})$ (for each $i$) assuming $P(x_j)$ for all $j$ where $\tau_{i,j} =$~\('a\<^sub>1,\,'a\<^sub>n)t\. Distinctness and injectivity are applied automatically by \auto\ and other proof methods. Induction must be applied explicitly. Like in functional programming languages, datatype values can be taken apart with case expressions\index{case expression}\index{case expression@\case ... of\}, for example \begin{quote} \noquotes{@{term[source] "(case xs of [] \ 0 | x # _ \ Suc x)"}} \end{quote} Case expressions must be enclosed in parentheses. As an example of a datatype beyond \<^typ>\nat\ and \list\, consider binary trees: \ datatype 'a tree = Tip | Node "'a tree" 'a "'a tree" text\with a mirror function:\ fun mirror :: "'a tree \ 'a tree" where "mirror Tip = Tip" | "mirror (Node l a r) = Node (mirror r) a (mirror l)" text\The following lemma illustrates induction:\ lemma "mirror(mirror t) = t" apply(induction t) txt\yields @{subgoals[display]} The induction step contains two induction hypotheses, one for each subtree. An application of \auto\ finishes the proof. A very simple but also very useful datatype is the predefined @{datatype[display] option}\index{option@\option\}\index{None@\<^const>\None\}\index{Some@\<^const>\Some\} Its sole purpose is to add a new element \<^const>\None\ to an existing type \<^typ>\'a\. To make sure that \<^const>\None\ is distinct from all the elements of \<^typ>\'a\, you wrap them up in \<^const>\Some\ and call the new type \<^typ>\'a option\. A typical application is a lookup function on a list of key-value pairs, often called an association list: \ (*<*) apply auto done (*>*) fun lookup :: "('a * 'b) list \ 'a \ 'b option" where "lookup [] x = None" | "lookup ((a,b) # ps) x = (if a = x then Some b else lookup ps x)" text\ Note that \\\<^sub>1 * \\<^sub>2\ is the type of pairs, also written \\\<^sub>1 \ \\<^sub>2\. Pairs can be taken apart either by pattern matching (as above) or with the projection functions \<^const>\fst\ and \<^const>\snd\: @{thm fst_conv[of x y]} and @{thm snd_conv[of x y]}. Tuples are simulated by pairs nested to the right: \<^term>\(a,b,c)\ is short for \(a, (b, c))\ and \\\<^sub>1 \ \\<^sub>2 \ \\<^sub>3\ is short for \\\<^sub>1 \ (\\<^sub>2 \ \\<^sub>3)\. \subsection{Definitions} Non-recursive functions can be defined as in the following example: \index{definition@\isacom{definition}}\ definition sq :: "nat \ nat" where "sq n = n * n" text\Such definitions do not allow pattern matching but only \f x\<^sub>1 \ x\<^sub>n = t\, where \f\ does not occur in \t\. \subsection{Abbreviations} Abbreviations are similar to definitions: \index{abbreviation@\isacom{abbreviation}}\ abbreviation sq' :: "nat \ nat" where "sq' n \ n * n" text\The key difference is that \<^const>\sq'\ is only syntactic sugar: after parsing, \<^term>\sq' t\ is replaced by \mbox{\<^term>\t*t\}; before printing, every occurrence of \<^term>\u*u\ is replaced by \mbox{\<^term>\sq' u\}. Internally, \<^const>\sq'\ does not exist. This is the advantage of abbreviations over definitions: definitions need to be expanded explicitly (\autoref{sec:rewr-defs}) whereas abbreviations are already expanded upon parsing. However, abbreviations should be introduced sparingly: if abused, they can lead to a confusing discrepancy between the internal and external view of a term. The ASCII representation of \\\ is \texttt{==} or \xsymbol{equiv}. \subsection{Recursive Functions} \label{sec:recursive-funs} Recursive functions are defined with \indexed{\isacom{fun}}{fun} by pattern matching over datatype constructors. The order of equations matters, as in functional programming languages. However, all HOL functions must be total. This simplifies the logic --- terms are always defined --- but means that recursive functions must terminate. Otherwise one could define a function \<^prop>\f n = f n + (1::nat)\ and conclude \mbox{\<^prop>\(0::nat) = 1\} by subtracting \<^term>\f n\ on both sides. Isabelle's automatic termination checker requires that the arguments of recursive calls on the right-hand side must be strictly smaller than the arguments on the left-hand side. In the simplest case, this means that one fixed argument position decreases in size with each recursive call. The size is measured as the number of constructors (excluding 0-ary ones, e.g., \Nil\). Lexicographic combinations are also recognized. In more complicated situations, the user may have to prove termination by hand. For details see~@{cite Krauss}. Functions defined with \isacom{fun} come with their own induction schema that mirrors the recursion schema and is derived from the termination order. For example, \ fun div2 :: "nat \ nat" where "div2 0 = 0" | "div2 (Suc 0) = 0" | "div2 (Suc(Suc n)) = Suc(div2 n)" text\does not just define \<^const>\div2\ but also proves a customized induction rule: \[ \inferrule{ \mbox{@{thm (prem 1) div2.induct}}\\ \mbox{@{thm (prem 2) div2.induct}}\\ \mbox{@{thm (prem 3) div2.induct}}} {\mbox{@{thm (concl) div2.induct[of _ "m"]}}} \] This customized induction rule can simplify inductive proofs. For example, \ -lemma "div2(n) = n div 2" +lemma "div2 n = n div 2" apply(induction n rule: div2.induct) txt\(where the infix \div\ is the predefined division operation) yields the subgoals @{subgoals[display,margin=65]} An application of \auto\ finishes the proof. Had we used ordinary structural induction on \n\, the proof would have needed an additional case analysis in the induction step. This example leads to the following induction heuristic: \begin{quote} \emph{Let \f\ be a recursive function. If the definition of \f\ is more complicated than having one equation for each constructor of some datatype, then properties of \f\ are best proved via \f.induct\.\index{inductionrule@\.induct\}} \end{quote} The general case is often called \concept{computation induction}, because the induction follows the (terminating!) computation. For every defining equation \begin{quote} \f(e) = \ f(r\<^sub>1) \ f(r\<^sub>k) \\ \end{quote} where \f(r\<^sub>i)\, \i=1\k\, are all the recursive calls, the induction rule \f.induct\ contains one premise of the form \begin{quote} \P(r\<^sub>1) \ \ \ P(r\<^sub>k) \ P(e)\ \end{quote} If \f :: \\<^sub>1 \ \ \ \\<^sub>n \ \\ then \f.induct\ is applied like this: \begin{quote} \isacom{apply}\(induction x\<^sub>1 \ x\<^sub>n rule: f.induct)\\index{inductionrule@\induction ... rule:\} \end{quote} where typically there is a call \f x\<^sub>1 \ x\<^sub>n\ in the goal. But note that the induction rule does not mention \f\ at all, except in its name, and is applicable independently of \f\. \subsection*{Exercises} \begin{exercise} Starting from the type \'a tree\ defined in the text, define a function \contents ::\ \<^typ>\'a tree \ 'a list\ that collects all values in a tree in a list, in any order, without removing duplicates. Then define a function \sum_tree ::\ \<^typ>\nat tree \ nat\ that sums up all values in a tree of natural numbers and prove \<^prop>\sum_tree t = sum_list(contents t)\ (where \<^const>\sum_list\ is predefined). \end{exercise} \begin{exercise} Define the two functions \pre_order\ and \post_order\ of type @{typ "'a tree \ 'a list"} that traverse a tree and collect all stored values in the respective order in a list. Prove \<^prop>\pre_order (mirror t) = rev (post_order t)\. \end{exercise} \begin{exercise} Define a function \intersperse ::\ \<^typ>\'a \ 'a list \ 'a list\ such that \intersperse a [x\<^sub>1, ..., x\<^sub>n] = [x\<^sub>1, a, x\<^sub>2, a, ..., a, x\<^sub>n]\. Now prove that \<^prop>\map f (intersperse a xs) = intersperse (f a) (map f xs)\. \end{exercise} \section{Induction Heuristics}\index{induction heuristics} We have already noted that theorems about recursive functions are proved by induction. In case the function has more than one argument, we have followed the following heuristic in the proofs about the append function: \begin{quote} \emph{Perform induction on argument number $i$\\ if the function is defined by recursion on argument number $i$.} \end{quote} The key heuristic, and the main point of this section, is to \emph{generalize the goal before induction}. The reason is simple: if the goal is too specific, the induction hypothesis is too weak to allow the induction step to go through. Let us illustrate the idea with an example. Function \<^const>\rev\ has quadratic worst-case running time because it calls append for each element of the list and append is linear in its first argument. A linear time version of \<^const>\rev\ requires an extra argument where the result is accumulated gradually, using only~\#\: \ (*<*) apply auto done (*>*) fun itrev :: "'a list \ 'a list \ 'a list" where "itrev [] ys = ys" | "itrev (x#xs) ys = itrev xs (x#ys)" text\The behaviour of \<^const>\itrev\ is simple: it reverses its first argument by stacking its elements onto the second argument, and it returns that second argument when the first one becomes empty. Note that \<^const>\itrev\ is tail-recursive: it can be compiled into a loop; no stack is necessary for executing it. Naturally, we would like to show that \<^const>\itrev\ does indeed reverse its first argument provided the second one is empty: \ lemma "itrev xs [] = rev xs" txt\There is no choice as to the induction variable: \ apply(induction xs) apply(auto) txt\ Unfortunately, this attempt does not prove the induction step: @{subgoals[display,margin=70]} The induction hypothesis is too weak. The fixed argument,~\<^term>\[]\, prevents it from rewriting the conclusion. This example suggests a heuristic: \begin{quote} \emph{Generalize goals for induction by replacing constants by variables.} \end{quote} Of course one cannot do this naively: \<^prop>\itrev xs ys = rev xs\ is just not true. The correct generalization is \ (*<*)oops(*>*) lemma "itrev xs ys = rev xs @ ys" (*<*)apply(induction xs, auto)(*>*) txt\ If \ys\ is replaced by \<^term>\[]\, the right-hand side simplifies to \<^term>\rev xs\, as required. In this instance it was easy to guess the right generalization. Other situations can require a good deal of creativity. Although we now have two variables, only \xs\ is suitable for induction, and we repeat our proof attempt. Unfortunately, we are still not there: @{subgoals[display,margin=65]} The induction hypothesis is still too weak, but this time it takes no intuition to generalize: the problem is that the \ys\ in the induction hypothesis is fixed, but the induction hypothesis needs to be applied with \<^term>\a # ys\ instead of \ys\. Hence we prove the theorem for all \ys\ instead of a fixed one. We can instruct induction to perform this generalization for us by adding \arbitrary: ys\\index{arbitrary@\arbitrary:\}. \ (*<*)oops lemma "itrev xs ys = rev xs @ ys" (*>*) apply(induction xs arbitrary: ys) txt\The induction hypothesis in the induction step is now universally quantified over \ys\: @{subgoals[display,margin=65]} Thus the proof succeeds: \ -apply auto +apply(auto) done text\ This leads to another heuristic for generalization: \begin{quote} \emph{Generalize induction by generalizing all free variables\\ {\em(except the induction variable itself)}.} \end{quote} Generalization is best performed with \arbitrary: y\<^sub>1 \ y\<^sub>k\. This heuristic prevents trivial failures like the one above. However, it should not be applied blindly. It is not always required, and the additional quantifiers can complicate matters in some cases. The variables that need to be quantified are typically those that change in recursive calls. \subsection*{Exercises} \begin{exercise} Write a tail-recursive variant of the \add\ function on \<^typ>\nat\: \<^term>\itadd :: nat \ nat \ nat\. Tail-recursive means that in the recursive case, \itadd\ needs to call itself directly: \mbox{\<^term>\itadd (Suc m) n\} \= itadd \\. Prove \<^prop>\itadd m n = add m n\. \end{exercise} \section{Simplification} So far we have talked a lot about simplifying terms without explaining the concept. \conceptidx{Simplification}{simplification} means \begin{itemize} \item using equations $l = r$ from left to right (only), \item as long as possible. \end{itemize} To emphasize the directionality, equations that have been given the \simp\ attribute are called \conceptidx{simplification rules}{simplification rule}. Logically, they are still symmetric, but proofs by simplification use them only in the left-to-right direction. The proof tool that performs simplifications is called the \concept{simplifier}. It is the basis of \auto\ and other related proof methods. The idea of simplification is best explained by an example. Given the simplification rules \[ \begin{array}{rcl@ {\quad}l} \<^term>\0 + n::nat\ &\=\& \n\ & (1) \\ \<^term>\(Suc m) + n\ &\=\& \<^term>\Suc (m + n)\ & (2) \\ \(Suc m \ Suc n)\ &\=\& \(m \ n)\ & (3)\\ \(0 \ m)\ &\=\& \<^const>\True\ & (4) \end{array} \] the formula \<^prop>\0 + Suc 0 \ Suc 0 + x\ is simplified to \<^const>\True\ as follows: \[ \begin{array}{r@ {}c@ {}l@ {\quad}l} \(0 + Suc 0\ & \leq & \Suc 0 + x)\ & \stackrel{(1)}{=} \\ \(Suc 0\ & \leq & \Suc 0 + x)\ & \stackrel{(2)}{=} \\ \(Suc 0\ & \leq & \Suc (0 + x))\ & \stackrel{(3)}{=} \\ \(0\ & \leq & \0 + x)\ & \stackrel{(4)}{=} \\[1ex] & \<^const>\True\ \end{array} \] Simplification is often also called \concept{rewriting} and simplification rules \conceptidx{rewrite rules}{rewrite rule}. \subsection{Simplification Rules} The attribute \simp\ declares theorems to be simplification rules, which the simplifier will use automatically. In addition, \isacom{datatype} and \isacom{fun} commands implicitly declare some simplification rules: \isacom{datatype} the distinctness and injectivity rules, \isacom{fun} the defining equations. Definitions are not declared as simplification rules automatically! Nearly any theorem can become a simplification rule. The simplifier will try to transform it into an equation. For example, the theorem \<^prop>\\ P\ is turned into \<^prop>\P = False\. Only equations that really simplify, like \<^prop>\rev (rev xs) = xs\ and \<^prop>\xs @ [] = xs\, should be declared as simplification rules. Equations that may be counterproductive as simplification rules should only be used in specific proof steps (see \autoref{sec:simp} below). Distributivity laws, for example, alter the structure of terms and can produce an exponential blow-up. \subsection{Conditional Simplification Rules} Simplification rules can be conditional. Before applying such a rule, the simplifier will first try to prove the preconditions, again by simplification. For example, given the simplification rules \begin{quote} \<^prop>\p(0::nat) = True\\\ \<^prop>\p(x) \ f(x) = g(x)\, \end{quote} the term \<^term>\f(0::nat)\ simplifies to \<^term>\g(0::nat)\ but \<^term>\f(1::nat)\ does not simplify because \<^prop>\p(1::nat)\ is not provable. \subsection{Termination} Simplification can run forever, for example if both \<^prop>\f x = g x\ and \<^prop>\g(x) = f(x)\ are simplification rules. It is the user's responsibility not to include simplification rules that can lead to nontermination, either on their own or in combination with other simplification rules. The right-hand side of a simplification rule should always be ``simpler'' than the left-hand side --- in some sense. But since termination is undecidable, such a check cannot be automated completely and Isabelle makes little attempt to detect nontermination. When conditional simplification rules are applied, their preconditions are proved first. Hence all preconditions need to be simpler than the left-hand side of the conclusion. For example \begin{quote} \<^prop>\n < m \ (n < Suc m) = True\ \end{quote} is suitable as a simplification rule: both \<^prop>\n and \<^const>\True\ are simpler than \mbox{\<^prop>\n < Suc m\}. But \begin{quote} \<^prop>\Suc n < m \ (n < m) = True\ \end{quote} leads to nontermination: when trying to rewrite \<^prop>\n to \<^const>\True\ one first has to prove \mbox{\<^prop>\Suc n < m\}, which can be rewritten to \<^const>\True\ provided \<^prop>\Suc(Suc n) < m\, \emph{ad infinitum}. \subsection{The \indexed{\simp\}{simp} Proof Method} \label{sec:simp} So far we have only used the proof method \auto\. Method \simp\ is the key component of \auto\, but \auto\ can do much more. In some cases, \auto\ is overeager and modifies the proof state too much. In such cases the more predictable \simp\ method should be used. Given a goal \begin{quote} \1. \ P\<^sub>1; \; P\<^sub>m \ \ C\ \end{quote} the command \begin{quote} \isacom{apply}\(simp add: th\<^sub>1 \ th\<^sub>n)\ \end{quote} simplifies the assumptions \P\<^sub>i\ and the conclusion \C\ using \begin{itemize} \item all simplification rules, including the ones coming from \isacom{datatype} and \isacom{fun}, \item the additional lemmas \th\<^sub>1 \ th\<^sub>n\, and \item the assumptions. \end{itemize} In addition to or instead of \add\ there is also \del\ for removing simplification rules temporarily. Both are optional. Method \auto\ can be modified similarly: \begin{quote} \isacom{apply}\(auto simp add: \ simp del: \)\ \end{quote} Here the modifiers are \simp add\ and \simp del\ instead of just \add\ and \del\ because \auto\ does not just perform simplification. Note that \simp\ acts only on subgoal 1, \auto\ acts on all subgoals. There is also \simp_all\, which applies \simp\ to all subgoals. \subsection{Rewriting with Definitions} \label{sec:rewr-defs} Definitions introduced by the command \isacom{definition} can also be used as simplification rules, but by default they are not: the simplifier does not expand them automatically. Definitions are intended for introducing abstract concepts and not merely as abbreviations. Of course, we need to expand the definition initially, but once we have proved enough abstract properties of the new constant, we can forget its original definition. This style makes proofs more robust: if the definition has to be changed, only the proofs of the abstract properties will be affected. The definition of a function \f\ is a theorem named \f_def\ and can be added to a call of \simp\ like any other theorem: \begin{quote} \isacom{apply}\(simp add: f_def)\ \end{quote} In particular, let-expressions can be unfolded by making @{thm[source] Let_def} a simplification rule. \subsection{Case Splitting With \simp\} Goals containing if-expressions are automatically split into two cases by \simp\ using the rule \begin{quote} \<^prop>\P(if A then s else t) = ((A \ P(s)) \ (~A \ P(t)))\ \end{quote} For example, \simp\ can prove \begin{quote} \<^prop>\(A \ B) = (if A then B else False)\ \end{quote} because both \<^prop>\A \ (A & B) = B\ and \<^prop>\~A \ (A & B) = False\ simplify to \<^const>\True\. We can split case-expressions similarly. For \nat\ the rule looks like this: @{prop[display,margin=65,indent=4]"P(case e of 0 \ a | Suc n \ b n) = ((e = 0 \ P a) \ (\n. e = Suc n \ P(b n)))"} Case expressions are not split automatically by \simp\, but \simp\ can be instructed to do so: \begin{quote} \isacom{apply}\(simp split: nat.split)\ \end{quote} splits all case-expressions over natural numbers. For an arbitrary datatype \t\ it is \t.split\\index{split@\.split\} instead of @{thm[source] nat.split}. Method \auto\ can be modified in exactly the same way. The modifier \indexed{\split:\}{split} can be followed by multiple names. Splitting if or case-expressions in the assumptions requires \split: if_splits\ or \split: t.splits\. \ifsem\else \subsection{Rewriting \let\ and Numerals} Let-expressions (\<^term>\let x = s in t\) can be expanded explicitly with the simplification rule @{thm[source] Let_def}. The simplifier will not expand \let\s automatically in many cases. Numerals of type \<^typ>\nat\ can be converted to \<^const>\Suc\ terms with the simplification rule @{thm[source] numeral_eq_Suc}. This is required, for example, when a function that is defined by pattern matching with \<^const>\Suc\ is applied to a numeral: if \f\ is defined by \f 0 = ...\ and \f (Suc n) = ...\, the simplifier cannot simplify \f 2\ unless \2\ is converted to \<^term>\Suc(Suc 0)\ (via @{thm[source] numeral_eq_Suc}). \fi \subsection*{Exercises} \exercise\label{exe:tree0} Define a datatype \tree0\ of binary tree skeletons which do not store any information, neither in the inner nodes nor in the leaves. Define a function \nodes :: tree0 \ nat\ that counts the number of all nodes (inner nodes and leaves) in such a tree. Consider the following recursive function: \ (*<*) datatype tree0 = Tip | Node tree0 tree0 (*>*) fun explode :: "nat \ tree0 \ tree0" where "explode 0 t = t" | "explode (Suc n) t = explode n (Node t t)" text \ Find an equation expressing the size of a tree after exploding it (\noquotes{@{term [source] "nodes (explode n t)"}}) as a function of \<^term>\nodes t\ and \n\. Prove your equation. You may use the usual arithmetic operators, including the exponentiation operator ``\^\''. For example, \noquotes{@{prop [source] "2 ^ 2 = 4"}}. Hint: simplifying with the list of theorems @{thm[source] algebra_simps} takes care of common algebraic properties of the arithmetic operators. \endexercise \exercise Define arithmetic expressions in one variable over integers (type \<^typ>\int\) as a data type: \ datatype exp = Var | Const int | Add exp exp | Mult exp exp text\ Define a function \noquotes{@{term [source]"eval :: exp \ int \ int"}} such that \<^term>\eval e x\ evaluates \e\ at the value \x\. A polynomial can be represented as a list of coefficients, starting with the constant. For example, \<^term>\[4, 2, -1, 3::int]\ represents the polynomial $4 + 2x - x^2 + 3x^3$. Define a function \noquotes{@{term [source] "evalp :: int list \ int \ int"}} that evaluates a polynomial at the given value. Define a function \noquotes{@{term[source] "coeffs :: exp \ int list"}} that transforms an expression into a polynomial. This may require auxiliary functions. Prove that \coeffs\ preserves the value of the expression: \mbox{\<^prop>\evalp (coeffs e) x = eval e x\.} Hint: consider the hint in Exercise~\ref{exe:tree0}. \endexercise \ (*<*) end (*>*)