examples/parity/PARITY.sml examples/parity/RESET_REG.sml examples/parity/RESET_PARITY.sml
PARITY.sml contains the HOL sessions in this
chapter; the files RESET_REG.sml and
RESET_PARITY.sml contain the solutions to the exercises
described in Section 7.5.in, an output out and the
specification that the nth output on out is
T if and only if there have been an even number of
T's input on in. A theory named
PARITY is constructed; this contains the specification
and verification of the device. All the ML input in the boxes below
can be found in the file parity/PARITY.sml. It is
suggested that the reader interactively input this to get a `hands on'
feel for the example.-, so lines beginning with
- are typed by the user and other lines are the
system's response.| 1 |
% hol.unquote Moscow ML version 1.44 (August 1999) ... [closing file "/local/scratch/mn200/Work/hol98/tools/unquote-init.sml"] - load "bossLib"; > val it = () : unit - open bossLib; |
PARITY is defined so that for n>0,
PARITY n f is true if the number of T's
in the sequence f(1), ... ,
f(n) is even.| 2 |
- val PARITY_def = Define`
(PARITY 0 f = T) /\
(PARITY(SUC n)f = if f(SUC n) then ~(PARITY n f) else PARITY n f)`;
Definition stored under "PARITY_def".
> val PARITY_DEF =
|- (!f. PARITY 0 f = T) /\
!n f. PARITY (SUC n) f =
(if f (SUC n) then ~PARITY n f else PARITY n f)
: thm
|
Define is to store the
definition of PARITY on the current theory with name
PARITY_def and to bind the defining theorem to the ML
variable with the same name. Notice that there are two name spaces
being written into: the names of constants in theories and the names
of variables in ML. The user is generally free to manage these names
however he or she wishes (subject to the various lexical
requirements), but a common convention is (as here) to give the
definition of a constant CON the name
CON_DEF in the theory and also in ML. Another
commonly-used convention is to use just CON for the
theory and ML name of the definition of a constant
CON. Unfortunately, the HOL system does not use a
uniform convention, but users are recommended to adopt one. In this
case Define has made one of the choices for us, but there are
other scenarios where have to choose the name used in the theory file.!t. out t = PARITY t inpIt is intuitively clear that this specification will be satisfied if the signal1 functions
inp and out satisfy2:out(0) = Tand
!t. out(t+1) = (if inp(t+1) then ~(out t) else out t)This can be verified formally in HOL by proving the following lemma:
!in out.
(out 0 = T) /\ (!t. out(SUC t) = (if inp(SUC t) then ~(out t) else out t))
==>
(!t. out t = PARITY t inp)
The proof of this is done by Mathematical Induction and, although
trivial, is a good illustration of how such proofs are done. The
lemma is proved interactively using HOL's subgoal package. The proof
is started by putting the goal to be proved on a goal stack using the
function g which takes a goal as argument.| 3 |
- g `!inp out.
(out 0 = T) /\
(!t. out(SUC t) = (if inp(SUC t) then ~(out t) else out t)) ==>
(!t. out t = PARITY t inp)`;
> val it =
Proof manager status: 1 proof.
1. Incomplete:
Initial goal:
!inp out.
(out 0 = T) /\
(!t. out (SUC t) = (if inp (SUC t) then ~out t else out t)) ==>
!t. out t = PARITY t inp
|
GEN_TAC) and then making the two conjuncts of the
antecedent of the implication into assumptions of the goal (with
STRIP_TAC). The ML function expand
takes a tactic and applies it to the top goal; the resulting subgoals
are pushed on to the goal stack. The message `OK..' is
printed out just before the tactic is applied. The resulting subgoal
is then printed.| 4 |
- expand(REPEAT GEN_TAC THEN STRIP_TAC);
OK..
1 subgoal:
> val it =
!t. out t = PARITY t inp
------------------------------------
0. out 0 = T
1. !t. out (SUC t) = (if inp (SUC t) then ~out t else out t)
|
t is done
using Induct, which does
induction on the outermost universally quantified variable.| 5 |
- expand Induct;
OK..
2 subgoals:
> val it =
out (SUC t) = PARITY (SUC t) inp
------------------------------------
0. out 0 = T
1. !t. out (SUC t) = (if inp (SUC t) then ~out t else out t)
2. out t = PARITY t inp
out 0 = PARITY 0 inp
------------------------------------
0. out 0 = T
1. !t. out (SUC t) = (if inp (SUC t) then ~out t else out t)
|
PARITY.| 6 |
- expand(ASM_REWRITE_TAC[PARITY_def]);
OK..
Goal proved.
[.] |- out 0 = PARITY 0 inp
Remaining subgoals:
> val it =
out (SUC t) = PARITY (SUC t) inp
------------------------------------
0. out 0 = T
1. !t. out (SUC t) = (if inp (SUC t) then ~out t else out t)
2. out t = PARITY t inp
|
| 7 |
- expand(ASM_REWRITE_TAC[PARITY_DEF]);
OK..
Goal proved.
[..] |- out (SUC t) = PARITY (SUC t) inp
Goal proved.
[..] |- !t. out t = PARITY t inp
> val it =
Initial goal proved.
|- !inp out.
(out 0 = T) /\
(!t. out (SUC t) = (if inp (SUC t) then ~out t else out t)) ==>
!t. out t = PARITY t inp
|
top_thm : unit -> thmreturns the theorem just proved (i.e. on the top of the theorem stack) in the current theory, and we bind this to the ML name UNIQUENESS_LEMMA.
| 8 |
- val UNIQUENESS_LEMMA = top_thm();
> val UNIQUENESS_LEMMA =
|- !inp out.
(out 0 = T) /\
(!t. out (SUC t) = (if inp (SUC t) then ~out t else out t)) ==>
!t. out t = PARITY t inp
: thm
|
T
is input. To make the implementation more interesting, it will be
assumed that registers `power up' storing F. Thus the
output at time 0 cannot be taken directly from a
register, because the output of the parity checker at time
0 is specified to be T. Another tricky
thing to notice is that if t>0, then the output of the
parity checker at time t is a function of the input at
time t. Thus there must be a combinational path from
the input to the output.T is input at
in, this stored value is complemented. Registers are assumed to
`power up' in a state in which they are storing F. The second
register (connected to ONE) initially outputs
F and
then outputs T forever. Its role is just to ensure that the
device
works during the first cycle by connecting the output out to the
device ONE via the lower multiplexer. For all subsequent cycles
out is connected to l3 and so either carries the
stored parity value (if the current input is F) or the
complement of this value (if the current input is T).
ONE is true
of a signal out if for all times t the value of
out is T.| 9 |
- val ONE_def = Define `ONE(out:num->bool) = !t. out t = T`; Definition stored under "ONE_def". > val ONE_def = |- !out. ONE out = !t. out t = T : thm |
ONE_def' is used both
as an ML variable and as the name of the definition in the theory.
Note also how `:num->bool' has been added to resolve
type ambiguities; without this (or some other type information) the
typechecker would not be able to infer that t is to have
type num.NOT is true of a pair of signals
(inp,out) if the value of out is always
the negation of the value of inp. Inverters are thus
modelled as having no delay. This is appropriate for a
register-transfer level model, but not at a lower level.| 10 |
- val NOT_def = Define`NOT(inp, out:num->bool) = !t. out t = ~(inp t)`; Definition stored under "NOT_def". > val NOT_def = |- !inp out. NOT (inp,out) = !t. out t = ~inp t : Thm.thm |
sw selects which of the other
two inputs are to be connected to the output out.| 11 |
- val MUX_def = Define`
MUX(sw,in1,in2,out:num->bool) =
!t. out t = if sw t then in1 t else in2 t`;
Definition stored under "MUX_def".
> val MUX_def =
|- !sw in1 in2 out.
MUX (sw,in1,in2,out) = !t. out t = (if sw t then in1 t else in2 t)
: thm
|
t+1 are
the values input at the preceding time t, except at
time 0 when the register outputs
F.3| 12 |
- val REG_def =
Define `REG(inp,out:num->bool) =
!t. out t = if (t=0) then F else inp(t-1)`;
Definition stored under "REG_def".
> val REG_def =
|- !inp out. REG (inp,out) = !t. out t =
(if t = 0 then F else inp (t - 1))
: thm
|
| 13 |
- val PARITY_IMP_def = Define
`PARITY_IMP(inp,out) =
?l1 l2 l3 l4 l5.
NOT(l2,l1) /\ MUX(inp,l1,l2,l3) /\ REG(out,l2) /\
ONE l4 /\ REG(l4,l5) /\ MUX(l5,l3,l4,out)`;
Definition stored under "PARITY_IMP_def".
> val PARITY_IMP_def =
|- !inp out.
PARITY_IMP (inp,out) =
?l1 l2 l3 l4 l5.
NOT (l2,l1) /\ MUX (inp,l1,l2,l3) /\ REG (out,l2) /\ ONE l4 /\
REG (l4,l5) /\ MUX (l5,l3,l4,out)
: thm
|
|- !inp out. PARITY_IMP(inp,out) ==> (!t. out t = PARITY t inp)This states that if
inp and out
are related as in the schematic
diagram (i.e. as in the definition of PARITY_IMP),
then the
pair of signals (inp,out) satisfies the specification.UNIQUENESS_LEMMA by the
transitivity of ==>.| 14 |
- g `!inp out.
PARITY_IMP(inp,out) ==>
(out 0 = T) /\
!t. out(SUC t) = if inp(SUC t) then ~(out t) else out t`;
> val it =
Proof manager status: 2 proofs.
2. Completed: ...
1. Incomplete:
Initial goal:
!inp out.
PARITY_IMP (inp,out) ==>
(out 0 = T) /\
!t. out (SUC t) = (if inp (SUC t) then ~out t else out t)
|
STRIP_TAC. The rewriting tactic
PURE_REWRITE_TAC is used; this does no built-in
simplifications, only the ones explicitly given in the list of
theorems supplied as an argument. One of the built-in simplifications
used by REWRITE_TAC is |- (x = T) = x.
PURE_REWRITE_TAC is used to prevent rewriting with this
being done.
| 15 |
- expand(PURE_REWRITE_TAC
[PARITY_IMP_def, ONE_def, NOT_def, MUX_def, REG_def] THEN
REPEAT STRIP_TAC);
OK..
2 subgoals:
> val it =
out (SUC t) = (if inp (SUC t) then ~out t else out t)
------------------------------------
0. !t. l1 t = ~l2 t
1. !t. l3 t = (if inp t then l1 t else l2 t)
2. !t. l2 t = (if t = 0 then F else out (t - 1))
3. !t. l4 t = T
4. !t. l5 t = (if t = 0 then F else l4 (t - 1))
5. !t. out t = (if l5 t then l3 t else l4 t)
out 0 = T
------------------------------------
0. !t. l1 t = ~l2 t
1. !t. l3 t = (if inp t then l1 t else l2 t)
2. !t. l2 t = (if t = 0 then F else out (t - 1))
3. !t. l4 t = T
4. !t. l5 t = (if t = 0 then F else l4 (t - 1))
5. !t. out t = (if l5 t then l3 t else l4 t)
|
out 0 = T and its assumptions are equations relating
the values on the lines in the circuit. The natural next step would
be to expand the top goal by rewriting with the assumptions. However,
if this were done the system would go into an infinite loop because
the equations for out, l2 and
l3 are mutually recursive. Instead we use the
first-order reasoner PROVE_TAC to do the work:| 16 |
- expand(PROVE_TAC []);
OK..
Meson search level: .....
Goal proved.
[......] |- out 0 = T
Remaining subgoals:
> val it =
out (SUC t) = (if inp (SUC t) then ~out t else out t)
------------------------------------
0. !t. l1 t = ~l2 t
1. !t. l3 t = (if inp t then l1 t else l2 t)
2. !t. l2 t = (if t = 0 then F else out (t - 1))
3. !t. l4 t = T
4. !t. l5 t = (if t = 0 then F else l4 (t - 1))
5. !t. out t = (if l5 t then l3 t else l4 t)
|
out(SUC t), is expanded using the assumption:!t. out t = if l5 t then l3 t else l4 tHowever, if this assumption is used for rewriting, then all the subterms of the form
out t will also be expanded.
To prevent this, we really want to rewrite with a formula that is
specifically about out (SUC t). We want to somehow
pull the assumption that we do have out of the list and rewrite
with a specialised version of it. We can do just this using
PAT_ASSUM. This tactic is of type term -> thm
-> tactic. It selects an assumption that is of the form given
by its term argument, and passes it to the second argument, a
function which expects a theorem and returns a tactic. Here it is
in action:| 17 |
- e (PAT_ASSUM ``!t. out t = X t``
(fn th => REWRITE_TAC [SPEC ``SUC t`` th]))
<<HOL message: inventing new type variable names: 'a, 'b.>>
OK..
1 subgoal:
> val it =
(if l5 (SUC t) then l3 (SUC t) else l4 (SUC t)) =
(if inp (SUC t) then ~out t else out t)
------------------------------------
0. !t. l1 t = ~l2 t
1. !t. l3 t = (if inp t then l1 t else l2 t)
2. !t. l2 t = (if t = 0 then F else out (t - 1))
3. !t. l4 t = T
4. !t. l5 t = (if t = 0 then F else l4 (t - 1))
|
``\x. if l5 x then l3 x else l4 x``.| 18 |
- e (RW_TAC arith_ss []);
OK..
Goal proved.
[.....]
|- (if l5 (SUC t) then l3 (SUC t) else l4 (SUC t)) =
(if inp (SUC t) then ~out t else out t)
Goal proved.
[......] |- out (SUC t) = (if inp (SUC t) then ~out t else out t)
> val it =
Initial goal proved.
|- !inp out.
PARITY_IMP (inp,out) ==>
(out 0 = T) /\
!t. out (SUC t) = (if inp (SUC t) then ~out t else out t)
|
PARITY_LEMMA and saved in the current theory.| 19 |
- val PARITY_LEMMA = top_thm ();
> val PARITY_LEMMA =
|- !inp out.
PARITY_IMP (inp,out) ==>
(out 0 = T) /\
!t. out (SUC t) = (if inp (SUC t) then ~out t else out t)
|
PARITY_LEMMA could have been proved in one step with a
single compound tactic. Our initial goal can be expanded with a
single tactic corresponding to the sequence of tactics that were used
interactively:| 20 |
- restart()
> ...
- e (PURE_REWRITE_TAC [PARITY_IMP_def, ONE_def, NOT_def,
MUX_def, REG_def] THEN
REPEAT STRIP_TAC THENL [
PROVE_TAC [],
PAT_ASSUM ``!t. out t = X t``
(fn th => REWRITE_TAC [SPEC ``SUC t`` th]) THEN
RW_TAC arith_ss []
]);
<<HOL message: inventing new type variable names: 'a, 'b.>>
OK..
Meson search level: .....
> val it =
Initial goal proved.
|- !inp out.
PARITY_IMP (inp,out) ==>
(out 0 = T) /\
!t. out (SUC t) = (if inp (SUC t) then ~out t else out t)
|
PARITY_LEMMA, the final theorem is easily
proved. This will be done in one step using the ML function
prove.| 21 |
- val PARITY_CORRECT = prove(
``!inp out. PARITY_IMP(inp,out) ==> (!t. out t = PARITY t inp)``,
REPEAT STRIP_TAC THEN MATCH_MP_TAC UNIQUENESS_LEMMA THEN
MATCH_MP_TAC PARITY_LEMMA THEN ASM_REWRITE_TAC []);
> val PARITY_CORRECT =
|- !inp out. PARITY_IMP (inp,out) ==> !t. out t = PARITY t inp
|
hol/examples/parity/RESET_REG.sml hol/examples/parity/RESET_PARITY.sml
ONE, NOT,
MUX and REG defined in
Section 7.3, design and verify a register
RESET_REG with an input in, reset line
reset, output out and behaviour
specified as follows.
reset is T at time
t, then the value at out at time
t is also T.
reset is T at time
t or t+1, then the value output at
out at time t+1 is T,
otherwise it is equal to the value input at time t on
inp.
RESET_REG(reset,inp,out) =
(!t. reset t ==> (out t = T)) /\
(!t. out(t+1) = ((reset t \/ reset(t+1)) => T | inp t))
Note that this specification is only partial; it doesn't specify the
output at time 0 in the case that there is no reset.RESET_REG_IMP as an existential quantification of a
conjunction of applications of ONE, NOT,
MUX and REG to suitable line
names,4 together with a proof of:RESET_REG_IMP(reset,inp,out) ==> RESET_REG(reset,inp,out)
The value at out is T if and only if there have been an even number of Ts input at inp since the last time that T was input at reset.
ONE, NOT,
MUX and REG defined in
Section 7.3.