This chapter defines refinement in Z, and shows how it derives from the relational model in Chap. 4. It discusses the similarities and differences with refinement in B.

The approach taken in a state-based specification language is very different in emphasis from that in a process algebra. Process algebras stress the interaction between independent components (processes), so that communication and concurrency are key. State is implicit in such a description. A different approach is taken by a state-based language which, as the name suggests, considers the specification of state as the primary focus in a description. In this chapter we illustrate the approach to state-based specification by briefly introducing the Z and B notations, and showing how we can apply our theory of refinement to them.

1 Z - The Language

Z is a state-based specification language. It uses first order predicate logic and set theory to describe the possible states of a system, including the initial state. There are a number of styles of using Z, and the one that we will illustrate here is the most common, the so-called states and operations style of using it. In this style a system is described by specifying operations which describe changes to the state of the system. The state of the system and the operations acting on it are written in Z using schemas which structure the specification into convenient components. A calculus is provided to combine the schemas in appropriate ways, and this schema calculus helps to structure the specification.

To illustrate its general approach we specify the frequency server in Z. The description is going to consist of

  • The types and global constants of the specification.

  • A description of the abstract state, written using schemas.

  • A description of the initial state of the system.

  • One or more operations, which define the behaviour of the system. Again these will be written using schemas and the schema calculus.

In Z the formal description can be interleaved with informal text, as we will do below.

Types. Every value in Z has a unique type; when x is declared as x:S then the type of x is the largest set containing S. In fact, Z provides a single built-in type, namely the type of integers . Although Z only provides a single pre-existing type, there are a number of ways for a specifier to define types relevant to the specification under construction.

One way to build further types is to simply declare them. A given set can be constructed through a declaration of the form

$$ [PID, FREQ] $$

which introduces two new types: the sets PID and FREQ. The internal structure of these types is left unspecified.

To specify global values we use an axiomatic definition, containing a declaration and a (optional) predicate that constrains the declaration. For example, the following

figure a

introduces a global constant F as a subset of FREQ.

1.1 Using Schemas

To describe the state Z uses schemas which denote labelled products. Like axiomatic definitions they consist of declarations and optional predicates. So

figure b

introduces a state consisting of two components: clients and freq, with no further predicates to constrain them. The operations will describe how they alter these state variables. First the initialisation is given by a special Init schema:

figure c

Here we are using the standard Z convention that the prime on a variable (e.g., as in \(freq'\)) denotes its after state. The use of \(State'\) (so State with a prime symbol) here means to include all declarations and predicates of the schema State, with all variables and all occurrences of them in predicates decorated with that prime.

So the effect of this initialisation is to constrain the initial value of freq to be the set F. The behaviour of the system is described by specifying operations which change the state. The following describes an operation called Allocate - which allocates a frequency.

figure d

The inclusion of \(\varDelta State\) means that the schema includes both the before state State and the after state \(State'\). Inputs and outputs to an operation are given in the declaration, so the above contains an input pid? and two outputs reply! and freq! (note the ! is part of the name, so that freq! and freq refer to different things). The constraints on these are given by the predicate below the line. Here this says that the input pid? must be one of the clients. Then after the operation clients is not altered, but a frequency freq! is allocated if freq is non-empty (if it is empty an error is reported via reply!). This frequency is removed from the state variable freq. In this description we use a type which has the obvious interpretation.

In a similar way one can specify the operation that deallocates a frequency as follows:

figure e

The effect of this operation is to take in two inputs, the operation being applicable whenever \(pid? \in clients\). The effect of the operation is that clients is unchanged, but that the input frequency is added to the set freq.

1.2 The Z Schema Calculus

Much of the power of Z as a specification notation derives from the operators that are provided for combining schemas. This schema calculus provides the ability to specify a complex operation by combining smaller components using schema conjunction, disjunction etc. Here we don’t have occasion to use the schema calculus as a structuring mechanism in a specification, we do, however, find it convenient as a way to express the refinement proof obligations directly in Z.

We have seen, by example, that schemas consist of a declaration and a predicate. We’ve also seen that schemas can be included in another schema. An example of this is the inclusion of \(\varDelta State\) in the definition of an operation schema. This can be extended to any schema where the effect of inclusion is obtained by expanding all declarations and conjoining all predicates.

We have also seen schema decoration in writing \(State'\). These are all part of the schema calculus. In addition, the schema calculus provides the schema operators of conjunction and implication which we will use in the refinement proof obligations.

The schema conjunction operator is closely related to schema inclusion, where the conjunction of two schemas S and T is identical to a schema which includes both S and T (and nothing else). The definition of implication is similar - but we make sure that all implicit constraints included in any typing information are included in the predicates of the schemas involved.

Schemas can also be used as declarations in quantification - again with the obvious interpretation. Thus we will write expressions such as

$$ \forall AState; CState \bullet \cdots $$

and

$$ \exists AState' \bullet \cdots $$

again with the obvious interpretation of quantification over all the variables in the named state spaces, as well as restricting the variables to those satisfying the predicates in the named state spaces.

A crucial use of the schema calculus is to define the precondition of a Z operation schema. This is defined as follows.

Definition 7.1

(The Z operation precondition)

For an operation Op on state State, with inputs Inps and outputs Outs, its precondition is defined by

   \(\square \)

Thus, preOp will be a schema on State and Inps indicating for which before-states and inputs Op provides a possible after-state and output.

For example,

(this incidentally illustrates that we can write our schemas in horizontal form as well as vertically).

We shall also find it convenient to extract the inputs and outputs of an operation in the following way:

Definition 7.2

(Input and output signature)

The input signature of an operation schema Op on state State is its projection on input components only. This is written ?Op.

The output signature of an operation schema Op on state State is its projection on output components only. This is written !Op.    \(\square \)

Thus \(?Deallocate = [ pid? : PID; freq? : FREQ]\).

Finally, we can use schemas as types. Our use of it here is restricted to using the \(\theta \) operator on the state State. Specifically, \(\theta State\) gives the mapping of values to components as a tuple of the labelled product that represents the schema. So, for example in a schema which contains \(\varDelta State\), the value \((\theta State, \theta State')\) is a pair, each element of which is a labelled tuple of the signature of State, one being the before state and the other the after state. Where we need to denote such tuples explicitly, they will look like : a tuple containing a Boolean field named b and an integer field named x.

Much more can be written about the Z schema calculus, we have given a brief introduction sufficient to motivate its use within the refinement framework. What should be clear throughout though, is the very different type of description one arrives at when using a state-based language such as Z compared to using a process algebra. None of the concerns of a process algebra: process, communication, concurrency ...., have been given an explicit treatment in Z. Instead it has concentrated on a description of the state and the effect an operation has on that state, including how the inputs and outputs are treated. There are some obvious parallels however, specifically, both styles are concerned with changes to a system upon occurrences of events or operations, and we will use this later as our starting point to reconcile the different semantic approaches to refinement. First, we consider how refinement is defined in Z. To do so we are going to take a Z specification and consider it as a data type in the following sense:

Definition 7.3

(Standard Z ADT)

A standard Z ADT is a 3-tuple \((State, Init, \{Op_i\}_{i\in I})\) such that State is a state schema, Init is a schema on \(State'\), and \(Op_i\) are operations on \(\varDelta State\). The index set I is the alphabet of the ADT.    \(\square \)

We now consider how we can apply our relational theory of refinement to it to derive proof obligations expressed in Z itself.

2 Z – Refinement

As we commented in the previous chapter, once the semantics of a language has been defined, the definition of refinement should follow naturally from it. For Z we will use a relational semantics as defined in Chap. 4 to define refinement. Specifically, we will consider a Z specification to define a relational data type in the sense of Sect. 4.2. The Z data type defines a state, an initialisation and a collection of operations which can be considered as relations between the before and after state.

Taking this approach means that refinement can then be defined in terms of programs in the manner we did in Definition 4.5. To do so we formalise the Z relational semantics in the following manner.

2.1 The Relational Semantics of a Z Specification

The relational data types we introduced earlier had the following form: \((\mathsf {State, Init, \{Op_i\}_{i \in I}, Fin})\), which used a global state \(\mathsf {G}\) whereas as we have just seen a Z specification can be thought of as defining a tuple

$$ (State, Init, \{ Op_i\}_{i \in I}) $$

of state, initialisation and operations. Whilst there might be an obvious correspondence between some of the components, we have not mentioned a finalisation when writing a Z description. Nor have we indicated how inputs and outputs are going to be embedded in a relational model that doesn’t normally have them.

In fact, the inputs and outputs of a specification are modelled by sequences whose elements are produced and consumed one by one by the operations of the ADT. Inputs and outputs are observable, and are thus part of the global state, as well as of the local state since they are consumed and produced by the individual operations. For simplicity, we assume that all operations \(Op_i\) have input of type Input, and output of type Output. Initialisation and finalisation will be concerned with copying the input and output sequences between the global and local state. We use a sans serif font to distinguish the relational interpretations from the Z specifications.

State The global state contains just sequences of inputs and outputs, whilst the local state contains both of those plus a representation of the Z ADT state:

figure f

Initialisation The initialisation transfers the sequence of inputs from the global state to the local state, and picks an initial local ADT state that satisfies the ADT’s initialisation.

figure g

Operations The effect of an operation Op is modelled as follows. The first element is taken from the input sequence, and used as the input for the operation. The remainder of the input sequence is left for the following operations. The output produced by the operation is appended to the output sequence. The state is transformed according to the operation.

figure h

Finalisation The finalisation makes visible the outputs produced by the program:

figure i

This is quite a significant choice, as one can see here clearly that you could have taken an alternative view of what would be visible. We will return to this point later. Sufficient to say for the moment the above is the ‘standard’ finalisation that produces the ‘standard’ definition of refinement in Z as we give it below.

2.2 Data Refinement and Simulations for Z

Now we have given a relational semantics to a Z specification, we can directly apply Definition 4.5. However, in the literature refinement in Z isn’t usually defined in those terms - but given in terms of the underlying simulations that, due to the theory in Chap. 4, are sound and complete for that definition of refinement.

However, the theory in Sect. 4.3 was defined for total relations - and the operations in Z aren’t necessarily total. For example, Deallocate has a precondition that \(pid? \in clients\). What happens if this does not hold? We know from Sect. 4.4 how to encode partial relations as total ones, and that is what we are going to do here. Note that here we have a choice, as Sect. 4.4 discussed two models with slightly different characteristics: the non-blocking interpretation and the blocking interpretation.

For Z we will use the non-blocking interpretation defined in Sect. 4.4.1.1, noting that other languages can make other choices. Specifically, Object-Z [1], an object oriented version of Z, uses the blocking interpretation. However, for the moment let us fix on the non-blocking interpretation. We know then that the precondition defines the region where an operation is defined, and that outside this region anything might happen. As we discussed in Sect. 4.4 the simulation rules can be unwound to define the forward and backward simulation rules for partial relations - see Definitions 4.12 and 4.13.

We start with two Z specifications given as data types \((AState, AInit, \{ AOp_i \}_{i \in I})\) and \((CState, CInit, \{ COp_i \}_{i \in I})\). To show a simulation between them it is necessary to postulate a retrieve relation R between the local state spaces AState and CState.

We can derive the relational interpretations of the specifications as above. Finally, we just need the relational interpretation of a retrieve relation which in the non-blocking model will be along the following lines.

As a retrieve relation does not normally refer to inputs and outputs, the most obvious interpretation is to assume identical inputs and outputs, which we do here. If R is the retrieve relation between AState and CState, the relational interpretation is:

figure j

Remember here that \(\theta State\) gives the mapping of values to components in the labelled product. Note further that this embedding of the retrieve relation confirms the requirement of conformity of inputs and outputs between concrete and abstract ADT.

We now consider the simulation conditions in turn. Although we haven’t stressed the use of the schema calculus in Z, one of the beauties of it is that we can turn the relational expressions into conditions in the Z schema calculus itself. We start with Definition 4.12 and represent the conditions directly in Z. First a forward simulation. The initialisation condition becomes

By construction of the finalisations in the embedding, it holds that

figure k

The applicability condition for operations can be re-expressed as follows:

figure l

Finally, for the correctness of operations, we have:

figure m

This derivation completes the full forward simulation rule for Z ADTs with inputs and outputs. This is expressed in the following definition.

Definition 7.4

(Forward simulation for Z)

Let \(A = (AState, AInit, \{ AOp_i \}_{i \in I})\) and \(C = (CState, CInit, \{ COp_i \}_{i \in I})\) be Z data types, where the operations have conformal inputs and outputs. The relation R on is a forward simulation from A to C if

and for all \(i\in I\):

figure n

If such a simulation exists, we also say that C is a forward simulation of A, also denoted , and similarly for corresponding operations of A and C.

   \(\square \)

We can make a similar derivation for backward simulations, resulting in the following set of conditions.

Definition 7.5

(Backward simulation for Z) Let \(A = (AState, AInit, \{ AOp_i \}_{i \in I})\) and \(C = (CState, CInit, \{ COp_i \}_{i \in I})\) be Z data types, where the operations have conformal inputs and outputs. Then the relation T on \(AState \wedge CState\) is a backward simulation from A to C if

figure o

and for all \(i \in I\):

figure p

If such a simulation exists, we also say that C is a backward simulation of A, also denoted , and similarly for corresponding operations of A and C.    \(\square \)

We have assumed that partial operations are to be interpreted in the non-blocking model. The alternative is to assume a blocking interpretation. If we do so then we can easily derive a set of forward and backward simulation conditions in the same way. When we do so the rules stay the same apart from the correctness condition. Specifically in the blocking model, the forward simulation correctness condition becomes

figure q

Similarly the backward simulation correctness condition becomes

figure r

2.3 Z Refinement in Practice

Having derived the simulation rules for Z it is worth considering how they are applied in practice. The first point to note is that a refinement is never verified by direct appeal to Definition 4.5. Verification according to that definition involves quantification over all possible programs. The whole point of simulations was that they reduced that to a condition applied on a per-operation basis. All Z refinements are verified in that manner. This involves postulating a retrieve relation - and of course one could make a mistake in the one that is chosen. One has to be careful to understand that just because a particular retrieve relation doesn’t verify a simulation, it doesn’t mean that no simulation exists but just that the chosen one doesn’t work.

We have seen that in a process algebra, refinement can allow a number of changes in a specification. Trace refinement allows one to refine all the behaviour away, however, other refinement relations that we have considered are usually centred around the reduction of non-determinism. The same is true of refinement in Z, in that:

Refinement in Z allows one to reduce non-determinism by weakening an operation’s precondition or strengthening an operation’s post condition.

Example 7.1

The Deallocate operation in our frequency server specification has a precondition that \(pid?\in clients\). We can weaken that to say what happens if - and here we say that in this case nothing in the state changes. So we produce a new more concrete specification with the same state and initialisation but with the new deallocate operation. That is we set:

figure s

with

figure t

Since the state spaces are the same we use the identity retrieve relation. That is, set R to be

figure u

We now apply one of the simulation rules. Here we just need a forward simulation. The initialisation condition is trivially true, as are the conditions needed for the Allocate operation since it is unchanged. For the Deallocate operation we note that is the condition \(pid? \in clients\) whereas is always true. Applicability follows trivially. Similarly for correctness we note that

trivially follows.    \(\square \)

There is another facet of refinement in Z which is due to the explicit representation of state in a Z specification. Specifically, although we have been explicit about the state in our specification, the state is internal, and the internal representation of a state is not part of the visible interface - none of it appears in the finalisation in the semantics. Thus this can be changed in a refinement - as long as the visible behaviour remains the same.

Example 7.2

In our frequency server freq was defined to be a set. In practice in an implementation some sort of queue or list is likely to be used. We introduce a new specification to reflect this design choice. The description is similar, except this version uses an injective sequence (, i.e. without duplicates) of frequencies as follows:

figure v
figure w
figure x

This time the retrieve relation is not the identity as it must document the change in the state space. We use the following

figure y

Using this the conditions for a forward simulation are easily verified.    \(\square \)

This illustrates the following

Refinement in Z allows one to change the state-space of a specification as long as the external behaviour remains consistent.

The description in Example 7.2 contains a lot of non-determinism. Specifically, the allocated frequency can be any of those in the sequence cfreq, and furthermore that list may be reordered in the deallocate operation. Both of these are unlikely in a real implementation. We can reduce this non-determinism by choosing the head of the sequence as the output frequency in the allocate operation. That is, replacing CAllocate by the following.

figure z

It is then a simple matter to verify the applicability and correctness conditions for this new operation schema.

Example 7.3

Simple refinements use retrieve relations which are actually functions, although this is not always the case as this nice example illustrates. Here there is one operation Pick that outputs an unused natural number. In the abstract specification this value is chosen non-deterministically.

figure aa
figure ab

In the refinement we use a single natural number n in place of the set s, now the operation Pick is no longer non-deterministic.

figure ac
figure ad

The retrieve relation between the two state spaces maps n to \(max(s) + 1\) if s is not empty, and n to 0 if s is empty.    \(\square \)

Of course, just as in the relational theory discussed in Sect. 4.4 not all refinements can be verified by a forward simulation. For example, it is easy to code up in Z the example illustrating this from Example 4.2 to specify an example that needs a backward simulation in its verification.

2.3.1 The Completeness of the Simulation Rules in Z

In Sect. 4.4.3 we discussed the circumstances by which the simulation rules were complete - that is whether every refinement can be verified using a combination of forward and backward simulation rules. Of course these results carry over to Z in the following way.

We know that simulations are jointly complete for total data types - however, it is rare that a Z specification only contains operations that are total. The use of Z forces to use one of the two partial interpretations: non-blocking or blocking. The two relevant results are the following:

Theorem 4.6 tells us that the non-blocking simulation rules are jointly complete in the non-blocking interpretation (i.e., the one that is standard in Z).

The counter-example in Sect. 4.4.3 shows us that the blocking simulation rules are not complete in the blocking interpretation - that is the interpretation used in, for example, Object-Z.

Example 7.4

A simple example is the following, with two data types A and C, where \(A=(State, AInit,\{ Dec \})\) and \(C=(State,CInit,\{ Dec \})\). They are mutual refinements in the blocking relational embedding, but the refinement of A by C cannot be proved using the non-blocking simulation rules in Z.

figure ae

3 The B-Method

B is another state-based notation which has a well-defined refinement methodology. Indeed, it is called the B-method to stress the methodology of development as opposed to just a language for formalising system descriptions. The notation it uses to describe systems is known as the Abstract Machine Notation, or AMN, and provides for a common framework for specifications, refinements and implementations. The style of AMN is similar to Z but is closer to a pseudo programming language with the use of constructs familiar from an imperative style of programming as well as using first order predicate logic and set theory. Its method is based on step-wise refinement from abstract descriptions down to descriptions which are close to code. To aid development the method has been supported by a variety of toolsets, developed alongside the language and very much part of its development methodology.

The basic building block of a specification in the B-notation is called an abstract machine. These can be used as components in larger specifications using various structuring mechanisms. Within a machine one declares variables, invariants, an initialisation and a collection of operations, in a way that is familiar to those knowing Z.

To illustrate the general approach we specify the frequency server using the B-notation. The specification of such a machine is as follows.

figure af

We discuss each part in turn.

Machines. Machines have specific names so they can be used as components in more complex descriptions, here we call this machine FreqServer. Parameters to machines can be specified after the machine name - its optional to have them, but in the above we have two in upper case, and these two correspond to the two given sets we used in Z. Parameters in lower case, here just one f, are values, they are given in lower case (hence we must depart from the Z here) and their types are given in the subsequent CONSTRAINTS clause which we give next - here describing the obvious thing. In addition to the two given sets, the Z specification gave an explicit type to the output reply!. Here we give that type the name REPLY and specify it in the SETS clause.

Variables. Variables, but without giving their types, are defined in the VARIABLES clause. Their types and any invariants that need to hold for the machine are given in the INVARIANT. The initialisation is given next. Here we can see the first use of programming pseudo-code, where the use of the after-state prime in Z is replaced by programming-like assignment.

Operations. Outputs and inputs of an operation Op are described by writing: \(outputs \leftarrow Op(inputs)\). So in the above, Deallocate(pid?, freq?) specifies an operation called Deallocate with two inputs but no outputs. Likewise \(reply!, freq! \leftarrow Allocate(pid?)\) says that Allocate has one input and two outputs. The decorations of ? and ! are not necessary in the B-notation, we have simply used them here to be consistent with the Z specification in Sect. 7.1.

Operations have explicit preconditions - listed under PRE. This is in contrast to Z where preconditions are implicit and need to be calculated if necessary using the schema operator . The effect of an operation is given after the THEN clause. Here we update the variables in the obvious fashion in both operations. The one piece of syntax and semantics to note is the expression \(freq! :\in freq\) which is the non-deterministic assignment of freq! to be any member of freq.

The B-notation also provides an explicit choice statement that could be used instead of the disjunction in the effect in the allocate operation. This would be written as

figure ag

There are further constructs available in the AMN, and, as commented above there are a variety of structuring mechanisms such as the ability to include one machine in another or use one in a different ways. We will not cover them here since they are not central to our theory of refinement, and the literature on B contains full descriptions of their use.

3.1 Machine Consistency

There are some explicit consistency conditions that a machine needs to fulfil to ensure a machine is internally coherent. These include checking that an invariant is consistent, that the initialisation actually establishes an initial state in which the invariant holds, and that the operations preserve the invariant. Finally, any parameters must meet the constraints. These conditions can be expressed as

figure ah

for a machine with variables v, invariant I, initialisation Init and for an operation with precondition P and effect S, parameters p and constraint C. Some of this notation needs explaining, and is known as the weakest precondition notation.

3.1.1 Weakest Preconditions

Informally the weakest precondition of a statement is the largest set of states from which that statement will reach a certain postcondition. So for a predicate P and statement S, [S]P is a predicate that holds for any state that is guaranteed to achieve P as its post-state. In the AMN that is used in B it is possible to calculate the weakest precondition for any statement S and postcondition P.

First note that it distributes through some of the propositional connectives, in that we have:

figure ai

The basic calculation one can make in AMN is the following:

for a variable x and expression E. Multiple assignment and the other AMN operators are similar, for example:

figure aj

very much along the lines one would expect.

With this notation in place one can see that

figure ak

requires, firstly, that under the context C the initialisation Init must establish the invariant I, and, secondly, that, under the context C invariants must be preserved by the operations.

There is much more to be said about the weakest precondition, its derivation and use in specification and development. However, for our purposes, this very brief introduction is sufficient to define refinement in the B-method, which we do now.

4 Refinement in the B-Method

The B-method comes with a well defined notion of refinement as part of its methodology, defined, not in terms of a semantic model, but in terms of some proof obligations as we now describe.

Refinements are specified in B via machines, known unsurprisingly as refinement machines, using a specific REFINEMENT header. A refinement machine has the same signature as the machine that it refines, in the same sense that refinement in Z preserved the operations with their input and output parameters. So again as one would expect, refinement in B is all about preserving observable behaviour whilst having the freedom to make some specific design and implementation choices. This immediately gives one a template for the refinement. So, for example, to refine the frequency server we know it will take the following form:

figure al

A refinement machine needs to describe the relationship between its state and the state of the machine that it refines, given as a linking invariant, which is another name for a retrieve relation. This is done explicitly within the refinement machine in its invariant.

The name of a refinement machine is not allowed to contain any parameters - the parameters of the original machine are visible to it. The convention is, like we have done here, to add an R to the name of the machine being refined.

The sets, constraints, and parameters of the machine being refined are visible to any refinement, but the state is not visible. Additional sets and constraints can be defined in the refinement machine, and, of course, it will have to define its own variables for its own state representation. These variables will need to be initialised, and additional invariants can be placed over them.

As in Z, refinement in B concerns the allowable changes of observable behaviour. Rather than a semantic interpretation being provided - and simulation rules being derived - proof obligations for refinement are given as the primary definition. These will be seen to correspond to those contained in the forward simulations that we discussed above. However, there are no rules that correspond to a backward simulation in the B-methodology.

All operations, with the same inputs and outputs, must also be given within the refinement. Applicability, as in Z, must hold for the operations. Specifically, operations in the refined machine must be enabled (i.e., their precondition be true) whenever the operation in the machine they refine is true. Similarly, correctness must hold. That is, when invoked within its precondition, the effect of an operation in a refined machine must correspond to some effect of the operation in the original machine, modulo the states being linked by the linking invariant. These conditions can be seen to be analogous to the definition of a forward simulation in Z.

Given this observation, it is unsurprising that refinement in the B-method allows non-determinism to be reduced in exactly the same way as a forward simulation does in Z. Non-determinism can also be ‘moved earlier’ in a refinement in the way that a forward simulation allows, but in the absence of any backward simulation proof obligations the methodology is not complete in the way that it is in Z.

Example 7.5

We can refine our frequency server machine in a similar fashion to how we performed the refinement in Z: replacing the set by an injective sequence, and making the operations deterministic. This is given as a refinement machine FreqServerR as follows.

figure am

The variables here are the variables needed in the state space of the concrete machine. They have the associated invariant that defines their types. In B we also include the invariant that defines the linking invariant, here (as in the Z) described by . Any sets and parameters of FreqServer are visible in FreqServerR, thus we can make reference here to PID, FREQ, f, and REPLY.

   \(\square \)

4.1 Proof Obligations for Refinement

To understand the proof obligations for refinement in B consider a machine Spec and its refinement SpecR.

figure an
figure ao

where the linking invariant between the two state spaces is called J.

Initialisation. As in the forward simulations we have met so far, every possible state that InitC can reach must be, modulo J some possible state that Init can reach. That is, in terms of weakest precondition, \(\lnot [Init]\ \lnot J\) must be true for any state that InitC can reach. This can written as:

$$ [InitC] \lnot [Init]\ \lnot J $$

A comment about non-termination is in order here. \(\lnot [Init]\ \lnot J\) might be true because some Init has some execution which does not terminate. However, we are only interested in machines that are internally consistent (in the sense discussed in Sect. 7.3.1), and one of the proof obligations there was that \([Init]\ I\). And if this holds then Init is guaranteed to terminate.

Operations. As in any forward simulation, we place a proof obligation on each operation. Specifically, in a way similar to a forward simulation in Z we require a correctness proof rule. The initial formulation of this is the following (in the absence of outputs)

However, if (as is usual) our operation has an output we must ensure that the outputs of the refinement are possible outputs of the original, recognising that, like reducing a non-deterministic after-state, we’re allowed to reduce non-determinism in the refinement of outputs. To do so we need to add some additional syntax in the above rules to record this:

In general in a refinement machine, it is not necessary to give an explicit precondition - as it inherits that of the machine being refined. However, in cases that it does, as in our abstract example above, we need to check that the precondition PC is true whenever P is true - modulo the invariant and linking invariant. This is expressed as:

$$ I \wedge J \wedge P \implies PC $$

Thus to summarise, machine SpecR is a valid refinement of machine Spec whenever:

figure ap

In this formulation we have just considered the refined initialisation and operations. In addition, a refinement machine might well have its own sets, constants and collection of properties on them. These introduce their own proof obligations - firstly by a consistency check between the two lots of sets, constants etc, and secondly by the necessity to add this information into the conditions above. We do not detail these here, since they don’t add to the essence of the refinement conditions discussed here. Full accounts are given in the literature.

Example 7.6

To show that FreqServerR is a refinement of FreqServer one first verifies the initialisation condition that

since is the linking invariant. This is easy to verify applying the law for weakest precondition calculation for assignment twice.

Then one has to show that the precondition in the refinement is true whenever it holds in the abstract specification. Since the preconditions for the two operations are the same in each specification this easily follows. Finally, one has to show correctness for each operation. For Deallocate, where we have no outputs, this amounts to showing that

figure aq

which is again true by direct calculation, and a similar proof for the case .    \(\square \)

4.2 Implementation Machines

The type of refinement we have just illustrated in Example 7.6 is a typical data refinement where we resolve some of the design decisions in moving the description closer to an implementation. It is, however, still some way from being at a level where one would start coding. The refinements in Z we discussed earlier had a similar flavour, and Z in this sense is very much a specification language. The B-method, on the other hand, goes further by providing an explicit methodology for refining down to code level. As part of this B has the concept of implementation machines, which is a specific kind of refinement machine from which code can be produced in a direct fashion.

To signify that this really is the implementation step, there can only be one implementation machine in any refinement derivation, whereas there can be as many refinement steps as needed. To ensure that implementation machines really are close to code they have to conform to some specific constraints, including the following:

  • Implementation machines have no state of their own

  • Developments are structured using a special imports clause

  • Implementation machines cannot use non-deterministic choice or parallel composition

Data refinement is supported by implementation machines in the same way as before - although the state is not directly included in the implementation machine itself.

5 Bibliographical Notes

There is a large literature on state-based languages and their use.

For example, introductory texts on Z include An Introduction to Formal Specification and Z by Potter et al [2] and books by Ratcliff [3], Bottaci and Jones [4], Bowen [5] and Jacky [6]. Specification Case Studies edited by Hayes [7] served as an early definition of the Z language and contains many examples of particular aspects of the notation. Spivey’s The Z Notation: A Reference Manual [8] came to be the de facto definition of the language, and until the standard [9] appeared, [10] was the main resource for Z semantics.

More advanced books include Z in Practice [11] by Barden, Stepney and Cooper aimed at those who already understood the basics of Z, and Using Z: Specification, Refinement and Proof [12] by Woodcock and Davies. The latter discusses proof and refinement in Z in some detail. The relationship between temporal logic and refinement in Z is discussed in [13, 14].

Refinement in Z and Object-Z [15] provides a comprehensive account of refinement in Z and Object-Z and its generalisations. The generalisations include non-atomic (or action) refinement whereby the conformality assumption is broken and one operation can be split into several upon refinement (see also [16, 17] and [18]); the consideration of internal operations in Z refinements (along the lines of the treatment in a process algebra, see [19, 20]); and notions of refinement where part of the internal state is made visible.

Z lacks the comprehensive tool-support that B and Event-B benefit from. There is, however, some work on tool support for the verification of refinements in Z, and this includes [21,22,23,24].

Introductory texts on B include the following: [25,26,27,28], which discuss both specification and refinement in B. A collection of case studies in B refinement is presented in [29]. Further examples of refinement in B are given in [28, 30]. B also benefits from extensive tool-support, including that provided by the B-toolkit as well as Atelier B. The B method has been used in some major safety-critical system applications in Europe (such as in Paris Metro Line 14 and Ariane 5 rocket).

The regular series of conferences, ZUM: The Z Formal Specification Notation ran throughout the 1990s [31,32,33,34,35,36] and has since developed into joint conferences with users of the B notation [37,38,39,40], and most recently also with ASM, Alloy and VDM in the ABZ series [41,42,43]. The FME (since 2005: FM) conferences also regularly contain papers concerning aspects of the notation.

There is a body of work on combining state-based and process algebraic languages to produce an integrated notation. These include combinations of CSP and Object-Z as well as CSP and B and so forth. The relationship between the different notions of refinement (see Part III) allows a well-defined notion of refinement to be defined in these integrated languages. For example, work on refinement in integrations of Object-Z and CSP includes [44,45,46,47,48]. A correspondence between B and action systems has been used as a vehicle to specify and refine concurrent systems, see, for example, [49].