The bounded model checking of our two invariants for the readers and
writers example up to depth
greatly increases our confidence
that the invariants hold, but, as already mentioned, bounded model
checking is an incomplete procedure that falls short of being a proof:
a counterexample at greater depth could exist.
Can we actually fully verify such invariants in an automatic way? One
possible method is to verify the invariants through search not on the
original infinite-state system, but on a finite-state
abstraction of it, that is, on an appropriate quotient of the
original system whose set of reachable states is finite. The paper
[62] describes a simple method for, given a
rewrite theory
, defining an
abstraction
of it: just add equations. That is,
we can define our abstract theory as a rewrite theory
, with
a set of extra equations powerful
enough to collapse the infinite set of reachable states into a finite
set. This method can be used (with an additional deadlock-freedom
requirement) to verify not just invariants, but in fact any LTL
formula (see [62] and Section 13.4 of
[16]).
Of course, the equations
we add cannot be arbitrary. First of
all, they should respect all the necessary executability assumptions,
so that in
the equations
should be ground Church-Rosser and terminating,9.2 and
the rules
should be ground coherent with
. Furthermore,
the equations
should be invariant-preserving with respect to
the invariants that we want to model check; that is, for any such invariant
I and for any two ground terms
denoting states such that
, it should be the case that
.
A first key observation is that, if both
and
protect the sort Bool, that is,
the only canonical forms of that sort are true and
false, and
, then the
equations
are invariant-preserving. A second key observation is that
we may be able to automatically check that a module protects the sort
Bool by:
For invariant verification, the key property that an abstraction
meeting these requirements satisfies is that, if I is one of
the invariants preserved by
, then we have the implication
We can illustrate these ideas by defining an abstraction of our readers-writers system. In order to check that the equations in our abstraction preserve the invariants, we need an explicit representation of those invariants. Since at present the CRC and MTT tools do not handle predefined modules like BOOL, we use instead a sort NewBool.
mod READERS-WRITERS-PREDS is
protecting READERS-WRITERS .
sort NewBool .
ops tt ff : -> NewBool [ctor] .
ops mutex one-writer : Config -> NewBool [frozen] .
eq mutex(< s(N:Nat), s(M:Nat) >) = ff .
eq mutex(< 0, N:Nat >) = tt .
eq mutex(< N:Nat, 0 >) = tt .
eq one-writer(< N:Nat, s(s(M:Nat)) >) = ff .
eq one-writer(< N:Nat, 0 >) = tt .
eq one-writer(< N:Nat, s(0) >) = tt .
endm
We can now define our abstraction, in which all the states having more than one reader and no writers are identified with the state having exactly one reader and no writer.
mod READERS-WRITERS-ABS is
including READERS-WRITERS-PREDS .
including READERS-WRITERS .
eq < s(s(N:Nat)), 0 > = < s(0), 0 > .
endm
where the second including importation is needed because the READERS-WRITERS module is not protected, but would be assumed protected by default (because it is protected in READERS-WRITERS-PREDS) unless we explicitly declare it in including mode (see Section 6.1.3).
In order to check both the executability and the invariant-preservation properties of this abstraction, since we have no equations with either tt or ff in their lefthand side, we now just need to check:
Regarding termination, since the equations of READERS-WRITERS-ABS contain those of the module READERS-WRITERS-PREDS, it is enough to check the termination of the equations in the former. The MTT tool, using the AProVE termination tool, checks this automatically.
Once the READERS-WRITERS-ABS and READERS-WRITERS-PREDS modules are available in Full Maude (see Section 14.1), we can check confluence of the equations by invoking the CRC as follows:
Maude> (check Church-Rosser READERS-WRITERS-PREDS .)
Church-Rosser checking of READERS-WRITERS-PREDS
Checking solution:
All critical pairs have been joined. The specification is
locally-confluent.
The specification is sort-decreasing.
Maude> (check Church-Rosser READERS-WRITERS-ABS .)
Church-Rosser checking of READERS-WRITERS-ABS
Checking solution:
All critical pairs have been joined. The specification is
locally-confluent.
The specification is sort-decreasing.
which finishes task (1).
Regarding (2), the SCC tool gives us:
Maude> (scc READERS-WRITERS-PREDS .)
Checking sufficient completeness of READERS-WRITERS-PREDS ...
Success: READERS-WRITERS-PREDS is sufficiently complete under the
assumption that it is weakly-normalizing, confluent, and
sort-decreasing.
Maude> (scc READERS-WRITERS-ABS .)
Checking sufficient completeness of READERS-WRITERS-ABS ...
Success: READERS-WRITERS-ABS is sufficiently complete under the
assumption that it is weakly-normalizing, confluent, and
sort-decreasing.
This leaves us with task (3), where the Coherence Checker (ChC) tool responds as follows:
Maude> (check coherence READERS-WRITERS-PREDS .)
Coherence checking of READERS-WRITERS-PREDS
Coherence checking solution:
All critical pairs have been rewritten and all equations
are non-constructor.
The specification is coherent.
Maude> (check coherence READERS-WRITERS-ABS .)
Coherence checking of READERS-WRITERS-ABS
Coherence checking solution:
The following critical pairs cannot be rewritten:
cp < s(0), 0 > => < s(N*@:Nat), 0 > .
Of course, ground coherence means that all ground instances of this pair can be rewritten by a one-step rewrite up to canonical form by the equations. We can reason by cases and decompose this critical pair into two:
cp < s(0), 0 > => < s(0), 0 > . cp < s(0), 0 > => < s(s(N:Nat)), 0 > .
Using the reduce command we can check that the
canonical form of the term < s(s(N:Nat)), 0 > is
< s(0), 0 >. Therefore, all we need to do is to check that
< s(0), 0 > rewrites to itself (up to canonical
form) in one step. We can do this check by giving the command:
Maude> search in READERS-WRITERS-ABS : < s(0), 0 > =>1 X:Config . Solution 1 (state 0) states: 1 rewrites: 2 in 0ms cpu (26ms real) (~ rews/sec) X:Config --> < s(0), 0 > Solution 2 (state 1) states: 2 rewrites: 3 in 0ms cpu (124ms real) (~ rews/sec) X:Config --> < 0, 0 > No more solutions.
We have therefore completed all the checks (1)-(3) and can now automatically verify the two invariants on the abstract system, and therefore conclude that they hold in our original infinite-state readers-writers system, by executing the search commands:
Maude> search in READERS-WRITERS-ABS :
< 0, 0 > =>* C:Config such that mutex(C:Config) = ff .
No solution.
states: 3 rewrites: 9 in 0ms cpu (0ms real) (~ rews/sec)
Maude> search in READERS-WRITERS-ABS :
< 0, 0 > =>* C:Config such that one-writer(C:Config) = ff .
No solution.
states: 3 rewrites: 9 in 0ms cpu (0ms real) (~ rews/sec)