Facilitating the Verification of Diffusing Computations and Their Applications


 
 
We study a class of distributed algorithms, generally known by the name of diffusing computa- tions, that play an important role in all kinds distributed and/or database applications to perform tasks like termination detection, leader election, or propagation of information with feedback. We construct a highly parameterized abstract algorithm and shown that many existing algorithms and their applications can be obtained from this abstract algorithm by instantiating the parameters appropriately and/or refining some of its actions. Subsequently, we show that this use of param- eterization and re-usability of notation and proof leads to a reduction of the effort and cost of developing and verifying distributed diffusing computations. More specific, we show that proving the correctness of any application now boils down to verifying an application-specific safety prop- erty and reusing the termination and safety proofs of the underlying abstract algorithm. 
 
 



Introduction
It is our belief that the use of parameterization and re-usability of notation and proof are not sufficiently used when specifying and proving distributed algorithms.This paper uses a family of diffusing computations to show how easily these concepts can be used during the specification of distributed algorithms.We will show how this can lead to better representations of distributed algorithms that: show similarities with (and differences from) related algorithms, increase pedagogical effectiveness, and reduce proof-effort of specific applications of these algorithms.The concepts used in this paper are not new, nor are the algorithms presented.However, specifying a class of distributed algorithms using parameterization and re-usability of notation and showing the many advantages stated above, has, in our opinion, not been sufficiently explored.[2,15]; global termination detection [11,6,3,14,20,21,28,29]; deadlock detection [4,19] (re-)synchronization [12,24]; distributed reset [1]; computing a function of which each node holds part of the input (e.g.summation, maximum finding, distributed approximation of network diameter) [21,29] and many other pyramidal functions [22].
Diffusing computations constitute an interesting case study since they are highly parallel and non-deterministic.Consequently, many have studied them.Less formal work includes [25,26,28,29].More formal work can be found in [6,2,20,15,8,9,18,17,30,13,10].Examining and studying all these works we concluded that specifications and correctness proofs are most of the time not easy to reuse in order to demonstrate the various applications of diffusing computations mentioned above.

Terminology and Notation
In this paper we use UNITY because it is simple to explain in a short paper like this.We could, however, have used any action-based formalism.Thus, we do not advocate that UNITY leads to better representations.Each formalism has its advantages and disadvantages, this paper does not go into these details.
UNITY [5] programs Π consist of a predicate (iniΠ) specifying the initial condition of the program, a set of variable declarations (vΠ), and a set of actions (aΠ) separated by the symbol [].Actions in a UNITY program (elements of the set Action) can be (multiple) assignments or guarded (if-then-else) actions.Simultaneous execution of assignments is modeled by , e.g. the action x := 1 y := 2 has the same effect as the multiple assignment x, y := 1, 2. A program execution is infinite, in each step an action is selected nondeterministically and executed.Selection is weakly fair, i.e. every action is selected infinitely often.
Program states will be modeled by functions from variables (Var) to values (Val).Statepredicates (elements of Pred) are sets of states modeled by functions from states to bool.Stateexpressions (elements of Expr) are functions from states to Val.When it is clear from the context, operators will be overloaded to denote their state-lifted counterparts.
A state-predicate J is stable in Π (denoted Π ⊢ J), if once J holds during the execution of Π, it will remain to hold.A stable predicate is an invariant when it also holds in the initial state of the program.
We will use the original UNITY [5] operators unless (to specify safety properties) and ensures (to specify one-step progress properties).To denote general progress properties, we will use Prasetya's reach () operator [23].The most important reason for this choice was the fact that Prasetya has embedded this UNITY variant in the HOL theorem prover [16,23].Using this HOL library with its wealth of pre-proved theorems and inference-rules, we saved a lot of time proving the diffusing computations.Details about these mechanical verification activities are outside the scope of this paper and can be found in [32].
Properties involving Prasetya's reach operator look like this: J P ⊢ p q and, intuitively, mean that if J is stable in program P , then P can make progress from J ∧ p to q.Like leadsto, the reach operator is defined as the smallest transitive and disjunctive closure of ensures.However, reach requires that the state-predicates p and q are restricted to be confined by the write variables of program P , which means that p q describes progress made through the writable part of a program.
Prasetya [23] also introduced a convergence operator denoted by J Π ⊢ p q. Intuitively, this means that a program Π converges from p to q under the stability of J, if, given that Π ⊢ J, program Π started in state-predicate p will eventually find itself in a situation where q holds and remains to hold.Laws about and , needed in this paper, are listed in Appendix 11 and taken from [23].
Function application is represented by a dot, e.g.s.x applies function s to x. Function composition is denoted by •.In proofs we will, when convenient, replace ∧ by a newline.For example: p ⇐ (some theorems and definitions) q ∧ r, will be written like: p ⇐ (some theorems and definitions) q r 4 The Communication Network The communication networks are connected centralized networks employing bi-directional asynchronous communication.These networks are modeled by a triple (P, neighs, starter), where: P is a finite set of nodes; neighs is a function that given some node p ∈ P, returns the set of neighbors of p, i.e. the set of nodes that are connected to p by a bi-directional communication link; and starter is a node in P that distinguishes itself from all other nodes (called the followers), in that it can spontaneously start the execution of its local algorithm (e.g. because it is triggered by some internal event).The followers can only start execution of their local algorithm after they have received a message from some neighbor.Such a network is connected if every pair of nodes is connected by a path of communication links.
For this paper it is sufficient to give an abstract model of asynchronous communication.We do this by just stating the functionality of the communication primitives.These primitives, listed below, only assign to a specially designated set of Communication Variables, denoted by CV.(P, neighs, starter): • mit.p.q (acronym for message in transit) can be used to check for a message in transit from p to q; • p nr sent to q, enables nodes to check how many messages they have already sent to a neighbor q; • p nr rec from q, to check the amount of messages received from q.
• send.p.q.m, implements that a node p sends message m to q; • The receive action contains two subtleties.First, it only has the desired effect when a message is indeed in transit.So the programmer has to ensure that this action is only executed after checking and confirming the availability of a message to receive.Second, when a node p receives a message m from say q, p usually does something with the received value(s) and stores the result(s) somewhere.Since we have no sequential composition of actions in UNITY, receive is parameterized with a function that when applied to p, q and m, returns an assignment that processes the received message and assigns the results to the appropriate variables.So receive.p.q.a implements that p receives a message m from q and processes it using assignment a.p.q.m.
Any program that wants to use these communication primitives should incorporate ASYNC Init.(P, neighs, starter) in its initial condition in order to initialize the communication variables.

Analyzing Diffusing Computations
Most of the diffusing computation algorithms found in literature more or less have the following behavior.A follower becomes active when it receives its very first message, and it marks the node from which it received this first message as its father.A non-idle node proceeds with two activities: [] q∈neighs.pif idle.p ∧ mit.q.p (idle) then receive.p.q.process father.p:= q idle.p if finished col and prop.p∧ ¬reported to father.p(done) then send.p.(father.p).mes propagation, i.e. sending a message to all its neighbors except its father; and collecting one message from each of its neighbors.When the starter has sent and received one message to and from all its neighbors (i.e. has completed propagating and collecting), it immediately is done, whereas a follower node p has completed propagating and collecting it first has to report to its father prior to becoming done.
The differences between the algorithms are in the communication protocols, more specifically when non-idle nodes are allowed to collect or propagate a message: • The ds algorithm allows a node to freely merge its propagating and collecting actions as long as it has not yet received messages from all its neighbors, and it has not yet sent to all its neighbors that are not its father.
• In the echo and pif algorithms, a non-idle node p can only receive a message after p has sent messages to all its neighbors except its father.So, the propagating activities must be completed before collecting information from non-father-neighbors.
• In the tarry algorithm, a non-idle node p can only propagate to a neighbor if the last event of p was a receive event; otherwise it has to wait until it receives something.So, the propagating and collecting activities strictly alternate.
• In the dfs algorithm, a non-idle node p in its propagating phase whose last event was receiving a message from some neighbor q: if p can propagate a message back to q, i.e. q is not p's father, and p has not yet sent to q, then p has to send a message back to this node q otherwise it can act like in tarry, and just pick any non-father-neighbor to which it has not yet sent a message.

Construct the Abstract Algorithm
Based on the described similarities, we can construct a first skeleton for all local algorithms (including the one for the starter).The skeleton is shown in Figure 1.The differences are clearly indicated by sub-scripting the program guards and part of the initial condition of which we have not yet given the exact characterization.Furthermore, the skeleton abstracts from specific applications of the algorithms by leaving the contents of the messages that are being sent, and the ways these are processed upon receipt, unspecified.These two aspects do not only increase the re-usability of the specification, it also gives the reader the opportunity to develop his or her own feeling about the possible uses of the algorithm.This improves pedagogical effectiveness because it can help the reader to better understand the existing applications, and can result in ingenious new ones.

Capturing the Similarities
Besides the similarities revealed by the skeleton in Figure 1, the analysis also taught us that: when a node is collecting this implies that it has not yet received messages from all its neighbors; when a node is propagating this implies that it has not yet sent to all its neighbors that are not its father; p can propagate to q when p has not yet sent to q, and q is not its father; when a node is finished col and prop, then it has received from all its neighbors and it has sent to all its non-father-neighbors; when a node has not yet reported to its father, it has not yet sent a message to its father ; when a node p is done it has sent and received a message to and from all of its neighbors (i.e.including its father).To capture these similarities we define the following predicates: rec from all neighs.p= ∀q ∈ neighs.p: p nr rec from q = 1 can propagate.p.q = p nr sent to q = 0 ∧ q = father.preported to father.p= (p nr sent to (father.p)= 1) ∨ (p = starter) sent to all neighs.p= ∀q ∈ neighs.p: p nr sent to q = 1 sent to all non fathers.p= ∀q ∈ neighs.p: q = father.p⇒ p nr sent to q = 1 finished col and prop.p= rec from all neighs.p∧ sent to all non fathers.pdone.p= rec from all neighs.p∧ sent to all neighs.p
In the tarry algorithm, a non-idle node p can only propagate to a neighbor if the last event of p was a receive event.We can represent this by introducing a new boolean-typed variable le rec.p (i.e. last event was a receive) for every node p, which we initialize to false for all nodes except the starter.In order to give the variable the right value, i.e. whether the last event of p was a receive event, we can add the assignment (le rec.p := true) to the then clauses of the actions {idle, col}, and (le rec.p := false) to the then clauses of {prop, done}.Finally, we can characterize collecting and propagating as follows: propagating tarry .p.q = ¬ sent to all non fathers.p∧ (le rec.p) collecting tarry .p.q = ¬ rec from all neighs.p∧ ¬(le rec.p) init tarry = init echo ∧ ∀p ∈ P : (p = starter) = (¬le rec.p) In order to adjust the skeleton from Figure 1 we synchronously superpose 4 functions upon it: idle a Π , col a Π , prop a Π ∈ P → P → Action, and done a Π ∈ P → Action, that specify additional functionality for each action in the skeleton.The result is in Figure 2. We can now complete the characterization of tarry by defining for all p, q ∈ P that idle a tarry .p.q and col a tarry .p.q equal le rec.p := true, and that prop a tarry .p.q and done a tarry .pequal le rec.p := false.Note that idle a.p.q, col a.p.q, prop a.p.q and done a.p for echo and DS are all skip.
In order to be able to formalize the dfs algorithm, we need to be able to remember the identity of the sender of the last incoming message.In order to make this possible, we introduce a new variable lp rec.p (last process of which p has received a message) for every node p. Then we define for all p ∈ P that idle a dfs .p.q and col a dfs .p.q equal le rec.p := true lp rec.p := q, and that prop a dfs .p.q and done a dfs .pare le rec.p := false.Thus, we define: propagating dfs .p.q = propagating tarry .p.q ∧ (q = lp rec.p ∨ ¬can propagate.p.(lp rec.p)) collecting dfs .p.q = collecting tarry .p.q init dfs = init tarry

Applications of Diffusing Computations
Precise discussion of the applications of diffusing computations requires us to specify the contents of the messages that are being sent and the ways these are processed upon receipt.Consequently, we parameterize our skeleton such that specific applications can be defined as instantiations of the underlying algorithm, and abstraction from applications is done by universal quantification over its arguments.In order to make the algorithms suitable for the characterization of specific applications, they will have to be parameterized with additional parameters: • a predicate initA∈ Expr, which can be used to specify an additional application specific initial condition.
• a function idle r ∈ P → P → Expr → Action, that specifies how an idle node should process messages upon receipt.
• a function col r ∈ P → P → Expr → Action, that specifies how a non-idle process should process messages upon receipt.
• a function prop mes∈ P → P → Expr, that, given a node p, specifies what message p should send to a neighbor in its propagating phase.
• a function done mes∈ P → Expr, that, given a node p, specifies which message p finally has to send to its father.
• again 4 functions like: idle a, col a, prop a Π ∈ P → P → Action, and done a ∈ P → Action, that specify additional functionality by synchronous superposition for each action in the skeleton but are independent of the underlying algorithm.
• a set of actions A that, by ways of asynchronous superposition, can be used to add additional functionality or behavior.
• a set of new variables V that are assigned by the superposed (synchronous and asynchronous) actions.

Termination of Diffusing Computations
Termination of diffusing computations means that when the algorithm is started in the initial state, eventually each node will reach the situation in which it neither sends nor receives any more messages, and all communication channels will be empty.Termination is independent of the contents of the messages that are being sent and how these are processed upon receipt.Suppose we have arbitrary initA, idle r, col r, prop mes, done mes, idle a, col a, prop aand done a.Let us abbreviate, for some Π ∈ {ds, echo, pif, tarry, dfs}: T Π = Π.initA.idler.col r.prop mes.done mes.idle a.col a.prop a.done a.A Termination for this family T Π of algorithms, for some invariant J Π of Π, is specified as: Evidently, when this specification has been proved for some invariant J Π of Π, it can be inferred for all possible combinations of initA, idle r, col r, prop mes, done mes, idle a, col a, prop a, done a, and A that do not assign to the underlying variables of Π.As will be shown, this significantly reduces the proof effort for specific applications of our base algorithms.The proof of specification (1) can be found in [32].
8 Propagation of Information with Feedback PIF (Propagation of Information with Feedback), is the problem of broadcasting a piece of information I to all nodes in a connected network in such a way that all the nodes "know" when they have finished participating in the broadcast, one node in particular (the starter) "knows" that the broadcast is completed, and upon completion all nodes own the piece of information I [25].A node p "knows" that it has finished participating in the broadcast when it has received and sent messages from and to all its neighbors, i.e. done.p.Moreover, when the starter is done it "knows" that the broadcast is completed.To make Π suitable for the PIF application, we introduce new local variables V.p for all processes p ∈ P and instantiate Π such that: (a) the starter initially has I stored in V.starter ; (b) upon receipt of a message m in the idle phase of node p, this value is copied directly to V.p ; (c) in the prop phase of node p, the value stored in (V.p) is sent ; (d) the contents of the messages received in the col phase are discarded ; (e) the contents of the messages sent in the done phase can remain unspecified.Now PIF can be defined as: PIF Π = Π.initA.idler.col r.prop mes.done mes.idle a.col a.prop a.done a.A.V where done mes is arbitrary, idle a = col a = prop a = done a = skip, A = ∅, V = {p ∈ P | V.p}, and: The formal specification of PIF applications reads: (∀p : p ∈ P : done.p)∧ (∀p : p ∈ P : (V.p) = I) where J PIF is an invariant of PIF Π stating additional safety behavior.Since PIF Π is an instantiation of Π, the correctness criterion above can be reduced: (2) ⇐ (Thm.3)(J Π ∧ J PIF ) PIFΠ ⊢ iniPIF Π (∀p : p ∈ P : done.p)(J Π ∧ J PIF ) PIFΠ ⊢ ∀p : p ∈ P : done.p∀p : p ∈ P : (V.p) = I The first conjunct is implied by specification (1) (use Thm.1, Thm.2, and the fact that iniPIF Π includes iniΠ).The second conjunct is application specific and, using Thm.4 and Thm.6, can be reduced to: PIFΠ ⊢ (J Π ∧ J PIF ) ∀p : p ∈ P : (J Π ∧ J PIF ∧ done.p⇒ (V.p) = I) The only thing left to do is find the characterization of the invariant J PIF that establishes the last conjunct.The most straightforward and evident choice is: (∀p : p ∈ P : done.p⇒ (V.p) = I).This is obviously a stable property, and sufficient to prove the conditions stated above.

Computation of Summation
Suppose that every node p ∈ P in the network has a unique local variable that stores some data value, and that the distribution of these local variables is given by a function V : P→Var.Imagine D : P→Val being a snapshot of the values that reside at the local variables in some state s, i.e.D = s • V .
It is straightforward [8,17,18,29,30] to instantiate our algorithm such that the sum of all data values is computed and eventually resides at the starter (i.e. is stored in the variable V.starter).Instantiate Π such that: D i is defined to be the initial distribution of the data values; in the (prop) phase of node p, the identity element of + (say 0) is sent; in the (done) phase of node p, (V.p) is sent; upon receiving a message m in the (idle) and (col) phase, this value m is added to the data value that resides in (V.p), and the result is stored in (V.p).We define the specific SUM-application of Π by S Π = Π.initA.idler.col r.prop mes.done mes.idle a.col a.prop a.done a.A.V where idle a=col a=prop a=done a=skip, A = ∅, V = {p ∈ P | V.p} and, for a commutative monoid (+, 0): The formal specification of these summation algorithms reads: where J S is an invariant stating additional safety behavior of S Π .We now reduce the correctness criterion for S Π in a way that progress properties already proved for Π are inherited and we only need to prove application specific properties.The first conjunct equals (1).Consequently, we are left with finding the characterization of invariant J S that establishes the last conjunct.Although some ingenuity is required in order to come up with this invariant, its construction is guided by the availability of information about its use within the process of verifying the above correctness criterion.Any message sent during the execution of the algorithm is: either 0 (in the prop phases), or the data value (V.p) residing at some node p (in the done phases).However, after sending (V.p), node p will be done.Hence, since 0 is the identity element of +, the desired sum SUM will, in any state, be: the sum of values that reside at the nodes that are not done, added to the sum of values that are in transit in s.The safety property J S in state s:

p + (the values in the communication channels in s)
With this definition we can prove the required application specific requirements from above.Note that invariant J Π is still unspecified at this point, and its precise characterization is not needed to be able to derive a proof strategy for the summation application.Consequently, any instantiation of the ds, echo, pif, tarry and dfs algorithms that maintains safety property J S can be used to compute the sum.This clear separation of progress and safety properties enables us to prove the correctness of any application by verifying the safety property of the application and inheriting the progress proof (including invariant J Π that was needed to prove this progress) of the underlying algorithm.
Note that in done mes and A we use the s and r values instead of s green and r green .We have to do this because UNITY does not have sequential composition of assignment statements.However, since the assignment statements are executed in parallel, the result is the same.
The formal specification of these algorithms reads: where J fcs is an invariant stating additional safety behavior of FCS Π .Again, we can reduce the correctness criterion for in a way that progress properties already proved for Π are inherited and we only need to prove application specific properties.This one-step progress properties is satisfied by the action in A, and so we are left with finding the characterization of invariant J fcs that enables us to prove this.Since four summations are being calculated simultaneously, we will have four invariants that are similar to the invariant of the summation algorithm from the previous section.More specifically, J fcs will be defined for all quadruples (X, c, x, n) ∈ {(S, red, s, 1), (S ′ , green, s, 2), (R, red, r, 3), (R ′ , green, r, 4)} as: With this definition of J fcs , we can prove the required application specific requirements from above.

Conclusion
We have given a highly parameterized abstract algorithm for diffusing computations, and we have shown that many existing algorithms and their applications can be obtained from this abstract algorithm by instantiating the parameters appropriately.By clearly separating the progress and safety properties of the different applications, we can proof the correctness of any application by verifying an application-specific safety property and inheriting the already proved termination and safety of the underlying algorithm.
Studies with postgraduate students have given empirical evidence that our re-usable specifications and proofs improve their understanding of the algorithms, increase the slope of the learningcurve, and enable students to prove the correctness of some specific applications within a remarkably short amount of time.

Figure 2 :
Figure 2: Augmented skeleton to deal with different communication strategies.