-- fullabs.csp
-- Illustrating full abstraction results for CSP
-- Part 2: discriminating via testing
-- Bill Roscoe
-- This file, and its companion,
-- illustrate the constructions used in Section 9.3 on
-- expressibility and full abstraction for CSP. The concepts
-- illustrated are sophisticated and remote from direct practical
-- applications. You are strongly advised not to try to follow these
-- files without studying the relevant section of the book!
-- BASICS
-- For simplicity and efficiency, we will deal only with the
-- case of a small fixed alphabet
channel a,b,c,fail,tick
-- The role of the event fail will be as in the text.
-- It is not permitted to refer to the actual tick event of CSP (written
-- as _tick when you see it in debuggers, etc) in
-- CSP scripts (because of the special role it has, only introduced via
-- SKIP), but we do need a way of referring to it in the semantic values
-- we manipulate in this script. Therefore the declared event tick
-- is included so we can handle semantic objects involving it. The
-- intention is that when it comes to mapping one of these (say the trace
-- ) to a real CSP process, that the corresponding behaviour of
-- that process would replace all tick's by the "real" event _tick
-- (i.e, this trace would become ).
-- The automatically-supported set Events equals {a,b,c,fail,tick}, of
-- course, but at the modelling level we want a set of communications
-- that doesn't include tick as an ordinary one
Sigma = diff(Events,{tick})
-- and an augmented one that does:
Sigmatick = Events
-- so the range of possible refusal sets (at the modelling level) is
RefSets = Set(Sigmatick)
--^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-^~-
-- DISCRIMINATING PROCESSES
-- The objective here is to show how simple tests can be used to
-- discriminate between any two processes that are not equivalent
-- in a given semantic model.
-- Traces model
-- What we do here is to show that any pair of processes with different
-- sets of traces are distinguished by some context relative to the
-- tests T1 failed when the process has the trace .
-- We can test for this property by seeing if our process trace-refines:
Test1 = ([] x:diff(Events,{fail}) @ x -> BotTM)
|~| SKIP
BotTM = ([] x:Events @ x -> BotTM)
|~| SKIP
-- The context that checks for a given trace using this test is
CT(s^,P) = ((TT(s^)[|Events|]P)\Events); fail -> STOP
CT(s,P) = (((TT(s)[|Events|]P)[[x <- a | x <- Events]])
[|{a}|]
FAIL(#s))\{a}
-- Where
-- A process that performs any given trace
TT(<>) = STOP
TT() = SKIP
TT(^s) = x -> TT(s)
FAIL(0) = fail -> STOP
FAIL(n) = a -> FAIL(n-1)
-- So for example the process
Q1 = ||| i:{1..3} @ a -> b -> a -> SKIP
-- has the trace but not so that
assert Test1 [T= CT(,Q1)
-- fails but not
assert Test1 [T= CT(,Q1)
-- similarly it fails
assert Test1 [T= CT(,Q1)
-- but not
assert Test1 [T= CT(,Q1)
---+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-
-- Stable failures model
-- The simply divergent process plays an important role in the
-- constructions for this model
DIV = SKIP;DIV
-- The test for this model is that our process cannot deadlock
-- immediately and doesn't have the trace . We can pick this
-- up via refinement of the process
Test2 = (|~| x:diff(Events,{fail}) @ x -> BotSFM)
|~| SKIP
BotSFM = ([] x:Events @ x -> BotSFM)
|~| SKIP
|~| STOP
-- We can test for occurrences of trace s wrt this test via the
-- context
CT'(s,P) = CT(s,P) ||| DIV
-- which never deadlocks because it never becomes stable.
-- As discussed in the text, it is sufficient thanks to the axioms to
-- use the above and a context that can detect a failure (s,X) where
-- s does not contain tick and X does:
CF(s,X,P) = ((P;DIV)[|Events|]FF(s,diff(Sigmatick,X)))\Events
-- Looking back to the example Q1, we can thus find that it has both
-- the trace
assert Test2 [F= CT'(,Q1)
-- and the failure (,{b,tick})
assert Test2 [F= CF(,{b,tick},Q1)
-- but not the failure (,{b,tick})
assert Test2 [F= CF(,{b,tick},Q1)
--<+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+>
-- Failures/divergences model
-- The test here is that the process neither deadlocks nor diverges immediately,
-- which we can tell by FD-refinement of the process
Test3 = (|~| x:Events @ x -> DIV)
|~| SKIP
-- Here we can detect divergence on the trace s (not including tick, which
-- is sufficient by D3) by the context
CD(s,P) = ((P[|Events|]TT(s))\Events)||| a -> STOP
-- the presence of a trace ending in tick
CT''(s^,P) = (((P[|Events|]TT(s^))\Events);DIV)||| a -> STOP
-- and of others
CT''(s,P) = ((P[|Events|]TT''(s))\Events)||| a -> STOP
TT''(<>) = DIV
TT''(^s) = x -> TT''(s)
-- the ||| a -> STOP clause is simply there to prevent the context failing
-- Test3 by deadlock: it can only fail by divergence.
-- The creation of contexts for failures (s,X) (where again tick is in X
-- but not in s) is in several stages:
-- First a process that can only deadlock after s if P has the failure (s,X)
CF1(s,{tick},P) = (P [|Events|] TT(s^));(a -> STOP)
CF1(s,X,P) = let A = diff(Sigma,X)
AS = []x:A @ a -> AS
within
((P;AS) [|Events|] FF'(s,A))
-- The above is slightly modified from the book to avoid choosing a member of
-- a set.
FF'(<>,X) = [] x:diff(Sigma,X) @ x -> STOP
FF'(^s,X) = x -> FF'(s,X)
-- And now checking for the presence of that deadlock:
CFD(s,X,P) = (CF1(s,X,P)[[x <- b | x <- Events]] [|{b}|] W(#s))\{b}
W(0) = b -> CS
W(n) = (b -> W(n-1)) [] CS
CS = c -> CS
-- We can repeat the tests we did over the stable failures model:
-- so that the following two fail Test3
assert Test3 [FD= CT''(,Q1)
assert Test3 [FD= CFD(,{b,tick},Q1)
-- but not
assert Test3 [FD= CFD(,{b,tick},Q1)
-- The divergence of
Q2 = (a -> b -> DIV)
[] (b -> Q2)
-- after but not after is demonstrated
assert Test3 [FD= CD(,Q2)
assert Test3 [FD= CD(,Q2)
--=|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|==|=
-- A few definitions from express.csp that are used above
TT'(<>) = DIV
TT'() = SKIP
TT'(^s) = DIV [] x -> TT'(s)
FF(<>,X) = [] x:diff(Sigma,X) @ x -> DIV
FF(^s,X) = DIV[] x -> FF(s,X)
--/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\
-- CAVEAT
-- At the time of writing, the semantics of termination in FDR does not
-- exactly coincide with that in the book. Specifically, it does not
-- adopt the principle enshrined in axiom F4 that a terminating process
-- can refuse any other event. This virtually never impacts on sensible
-- examples, because one never writes processes of the form SKIP [] P.
-- In the present file we are concerned with the fine-grain structure of
-- semantic models, however, and need to be able to handle all processes,
-- even SKIP [] P, properly.
-- In order to force FDR to conform to the semantics in the book we can,
-- however, use the operator when (rarely) you get one of these cases.
fixtick(P) = P;SKIP
-- Its job is to take a process P and produce one whose behaviour as
-- observed by
-- FDR is identical to that predicted for P by the book. Any ticks within
-- P are turned into taus followed by ticks in the sense discussed in
-- connection with Figure 7.2. The operator fixtick is, of course,
-- an identity operator in the semantics discussed in the book, so while
-- it may clutter definitions up it can do no harm to leave it in if the
-- semantics of FDR are subsequently changed to coincide with the book.
-- For example, the refinement
assert (a -> STOP) [] SKIP [FD= SKIP
-- predicted by the book may not hold, but
assert fixtick((a -> STOP) [] SKIP) [FD= SKIP
-- certainly will.
-- The consequence for this file is that on processes with external choice
-- between SKIP and something else you will not always get the expected
-- answers to tests on P, but should for fixtick(P).