Sie sind auf Seite 1von 210

Dipartimento di Informatica

Università di L’Aquila
Via Vetoio, I-67100 L’Aquila, Italy
http://www.di.univaq.it

Dissertation

Explicit Algorithms for Probabilistic Model Checking

Igor Melatti

June 2005

Advisor PhD Program Supervisor


Prof. B. Intrigila Prof. M. Flammini


c Igor Melatti, 2005. All rights reserved
A BSTRACT

In this Thesis we present two explicit algorithms for the verification of finite horizon properties of Proba-
bilistic Systems modeled by Discrete Time Markov Chains.

The first algorithm deals with finite horizon safety properties only. Thus, given a Markov Chain M and an
integer k (horizon), this algorithm is able to check whether the probability of reaching an error state of M
in at most k steps is below a given threshold.

On the other hand, the second algorithm is able to handle generic BPCTL (Bounded PCTL) formulas, i.e.
PCTL formulas in which all Until operators are bounded, possibly with different bounds. This entails that
we consider only system runs (paths) of bounded length. Thus, given a Markov Chain M and a BPCTL
formula Φ, our algorithm checks if Φ is satisfied in M. This allows to verify important properties, which
is not possible to check with the first algorithm, such as e.g. robustness in Discrete Time Stochastic Hybrid
Systems.

We present an implementation of our algorithms within a suitable extension of the Murϕ verifier. We call
FHP-Murϕ (Finite Horizon Probabilistic Murϕ) such extension of the Murϕ verifier.

Finally, we give experimental results comparing FHP-Murϕ with PRISM, a state-of-the-art symbolic model
checker for Markov Chains. Our experimental results show that FHP-Murϕ can effectively handle verifi-
cations for systems that are out of reach for PRISM, namely those involving arithmetic operations on the
state variables. However, PRISM is a more general verifier than Murϕ, since it handles also other Markov
Chain based models, and is able to verify also unbounded PCTL formulas.
ACKNOWLEDGMENTS

This Thesis has been realized thanks to the support of many people. First of all, I need to thank my advisor,
Prof. Benedetto Intrigila, together with Prof. Enrico Tronci, for their ideas and advices. But I cannot forget
Prof. Giuseppe Della Penna, for his help in the coding. Moreover, I have to thank my family, for their
patience, and my PhD Program companions, for the beautiful three years I’ve spent together with them.

Finally, I have to thank my thesis reviewers, namely Prof. Andrea Maggiolo Schettini, Prof. Ganesh
Gopalakrishnan and Xiaofang Chen for their suggestion on how to improve a previous version of this
Thesis.
TABLE OF C ONTENTS

Abstract i
Acknowledgments iii

Table of Contents iii


List of Figures vii

List of Tables ix
1 Introduction 1
1.1 Related Works . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.1 Probabilistic Model Checkers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Thesis Content Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 Theoretical Basis 5
2.1 Markov Chains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.1.1 “Real World” Markov Chains . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2 The BPCTL Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2.1 BPCTL Bounded Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3 Finite State/Discrete Time Stochastic Processes . . . . . . . . . . . . . . . . . . . . . . . 15
2.4 The Murϕ Verifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.4.1 Murϕ Input Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.4.2 A Toy Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3 Verification of Safety Properties 25


3.1 Finite Horizon Safety Verification of Markov Chains . . . . . . . . . . . . . . . . . . . . 25
3.2 Explicit Finite Horizon Safety Verification of Markov Chains . . . . . . . . . . . . . . . . 31
3.2.1 Algorithm Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.2.2 Algorithm Correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.2.3 Algorithm Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.3 Experimental Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.3.1 Probabilistic Dining Philosophers . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.3.2 A “Real world” Probabilistic Hybrid System . . . . . . . . . . . . . . . . . . . . 40

4 Verification of BPCTL Properties 43


4.1 Explicit BPCTL Model Checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.1.1 Algorithm Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.1.2 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
4.1.3 Algorithm Correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.1.4 Algorithm Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.2 Experimental Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.2.1 Probabilistic Dining Philosophers . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.2.2 A “Real World” Probabilistic Hybrid System . . . . . . . . . . . . . . . . . . . . 61

5 FHP-Murϕ Language Expressiveness 63


5.1 Markov Chains and PFSSs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
TABLE OF CONTENTS v

5.2 Probabilistic Rule Based Transition Systems . . . . . . . . . . . . . . . . . . . . . . . . . 64


5.3 From Communicating Stochastic Processes to PRBTS . . . . . . . . . . . . . . . . . . . . 65
5.4 Two protocols in FHP-Murϕ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.4.1 A Length-Based Queue System . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.4.2 A Time-Based Server-Queue System . . . . . . . . . . . . . . . . . . . . . . . . 69

6 Conclusions 75
6.1 Future Works . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
A FHP-Murϕ: the Input Language 77
A.1 An Overall Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
A.2 C++, Lex and Yacc Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
A.2.1 Modifications to mu.y . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
A.2.2 Modifications to mu.l . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
A.2.3 Modifications to lextable.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
A.2.4 Modifications to mu.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
A.2.5 Modifications to mu.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
A.2.6 Modifications to rule.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
A.2.7 Modifications to rule.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
A.2.8 Modifications to decl.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
A.2.9 Modifications to decl.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
A.2.10 Modifications to expr.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
A.2.11 Modifications to expr.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
A.2.12 Modifications to cpp code.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
B FHP-Murϕ: the Verifier 113
B.1 C++ Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
B.1.1 Modifications to mu prolog.inc . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
B.1.2 Modifications to mu epilog.ing . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
B.1.3 Modifications to mu real.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
B.1.4 Modifications to mu real.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
B.1.5 Modifications to mu io.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
B.1.6 Modifications to mu io.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
B.1.7 Modifications to mu util dep.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
B.1.8 Modifications to mu util.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
B.1.9 Modifications to mu verifier.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
B.1.10 Modifications to mu verifier.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
B.1.11 Modifications to mu state.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
B.1.12 Modifications to mu state.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
B.1.13 Added file splitFile.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
B.1.14 Added file splitFile.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
B.1.15 Added file mu probstack.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
B.1.16 Added file mu probstack.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
B.1.17 Modifications to mu system.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
B.1.18 Modifications to mu system.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

C PRISM code for LQS with 10 entries 183


References 195
L IST OF F IGURES

2.1 Graphical representation of the Markov Chain M of Example 2.2 . . . . . . . . . . . . . 7


2.2 A simple FHP-Murϕ code defining the PFSS of Example 2.3 . . . . . . . . . . . . . . . . 9
2.3 Graphical representation of the PFSS S of Example 2.3 . . . . . . . . . . . . . . . . . . . 10
2.4 Explicit Breadth–First Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.5 Murϕ execution model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.6 A Nondeterministic Finite State System . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.7 Murϕ code for the NFSS in Figure 2.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.8 Murϕ error trace for Murϕ model in Figure 2.7 . . . . . . . . . . . . . . . . . . . . . . . 23

3.1 Graphical representation of the Markov Chain M of Example 3.1 . . . . . . . . . . . . . 28


3.2 Computation of P [tt U≤k φ]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.3 Function Safety . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.4 Functions Insert and Checktable . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.5 Enqueue and Dequeue operations in the queue disk storage mechanism . . . . . . . . . 36
3.6 Pnueli-Zuck algorithm fragment to be modified in PRISM . . . . . . . . . . . . . . . . . 38
3.7 Pnueli-Zuck algorithm modified fragment in PRISM . . . . . . . . . . . . . . . . . . . . 38
3.8 BPCTL formula in PRISM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.9 Pnueli-Zuck algorithm in FHP-Murϕ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.10 Ruleset for TCS with probabilistic user demand . . . . . . . . . . . . . . . . . . . . . . . 41

4.1 Functions BPCTL, BPCTL rec and evalX . . . . . . . . . . . . . . . . . . . . . . . . . 45


4.2 Functions evalU and DF Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.3 Function try to evaluate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.4 Function insert cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.5 The PFSS of Example 4.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.6 The PFSS of Example 4.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.7 The PFSS of Example 4.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.8 Cache evolution for Example 4.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.9 Functions evalU nC and DF Search nC . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.10 Push and pop operations in the stack cycling mechanism . . . . . . . . . . . . . . . . . . 58

5.1 FHP-Murϕ implementation sketch for LQS . . . . . . . . . . . . . . . . . . . . . . . . . 68


5.2 FHP-Murϕ implementation sketch for TSQS (1) . . . . . . . . . . . . . . . . . . . . . . . 70
5.3 FHP-Murϕ implementation sketch for TSQS (2) . . . . . . . . . . . . . . . . . . . . . . . 71
5.4 PRISM implementation for LQS with 4 queue entries . . . . . . . . . . . . . . . . . . . . 73
L IST OF TABLES

3.1 Experimental results for the verification of a finite horizon safety property on the Pnueli-
Zuck protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.2 Experimental results for the verification of a finite horizon safety property on the Lehmann-
Rabin protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.3 Experimental results for the verification of a finite horizon safety property on the Turbogas
Control System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

4.1 Experimental results for the verification of a BPCTL property on the Pnueli-Zuck protocol
as it is found in the PRISM distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.2 Experimental results for the verification of a BPCTL property on the Lehmann-Rabin pro-
tocol as it is found in the PRISM distribution . . . . . . . . . . . . . . . . . . . . . . . . 60
4.3 Experimental results for the verification of a BPCTL property on the Pnueli-Zuck protocol
as it was modified in Section 3.3.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.4 Experimental results for the verification of a BPCTL property on the Lehmann-Rabin pro-
tocol as it was modified in Section 3.3.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.5 Experimental results for the verification of a BPCTL property on the Turbogas Control System 62
C HAPTER 1

I NTRODUCTION

Model checking techniques [6, 22, 33, 34, 47, 57] are widely used to verify correctness of digital hardware,
embedded software and protocols by modeling such systems as Nondeterministic Finite State Systems (in
the sequel, we refer to these kind of mathematical objects as NFSSs). In practice, NFSSs can be seen as
directed graphs where the edges indicate the allowed transitions between states.

However, there are many reactive systems that exhibit uncertainty in their behavior, i.e. which are stochastic
(of probabilistic) systems. This happens in fault tolerant systems, as well as in some kind of protocols, such
as randomized distributed protocols and communication protocols. Typically, stochastic systems cannot be
conveniently modeled using NFSSs. Thus, other mathematical objects have to be used, in order to take
into account the probabilistic aspect of these systems. Moreover, a model for stochastic systems has to be
such that the automatic analysis can be carried out in an efficient way. The simplest mathematical object
matching these requirements is the Discrete Time Markov Chain [3, 25] (in the sequel, just Markov Chain).
In fact, a Markov Chain can be seen as a directed graph labeled with outgoing probabilities on its edges, so
it is not too different from a NFSS. Obviously, not all stochastic systems can be modeled as Markov Chains,
since they may need to keep memory of their past, while Markov Chains are memoryless. Moreover, there
are mathematical objects which are derived from Markov Chains and are often used to model stochastic
systems:

Markov Decision Processes (MDP) allow to introduce nondeterminism in the definition of a Markov
Chain;
Continuous Time Markov Chains (CTMC) define an exponential probability distribution by allowing a
transition to delay by a given rate.

In this Thesis we restrict ourselves to systems which can be directly modeled as Markov Chain (Chapters 3
and 4), or have models that are translatable in a Markov Chain (Chapter 5).

Once the system has been modeled, standard model checking approach is to show that the NFSS S under
analysis satisfies a given property Φ, typically expressed in CTL (Computation Tree Logic [10]) or LTL
(Linear Time Logic, [11]), or their variants (such as ACTL or CTL∗ ). Moreover, if S does not satisfy Φ,
then a counterexample showing how S violates Φ is reported.

On the other hand, a property to be proved on a stochastic system has to deal with probabilities, which
are not expressible with CTL or LTL. To this end, Probabilistic Logics have been proposed, such as PCTL
(Probabilistic CTL [26]), used when the model is a Markov Chain or an MDP, or CSL (Continuous Stochas-
tic Logic [1]) which is used when the model is a CTMC. The collection of techniques whose goal is to state
if a given PCTL formula is satisfied in a given Markov Chain or MDP (or a CSL formula in a CTMC) is
called Probabilistic Model Checking. In practice, probabilistic model checking typically performs a quan-
titative verification, i.e. is typically used to prove that the executions leading to errors have an acceptable
probability to occur. For example, a probabilistic model checker may automatically verify a system property
like “the probability that a message is not delivered after 0.1 seconds is less than 0.20”. Thus, typically,

1
2 Chapter 1. Introduction

no counterexample is required if the given stochastic systems does not satisfy the given property. In fact, in
the above example, we already know that a message may be not delivered after 0.1 seconds, and we want
simply to know if this occurs with an acceptable probability (20%).

Note that, following [39, 40], we are using the expression “probabilistic model checking” to mean model
checking of probabilistic systems, while in [58] “probabilistic verification” has been used to indicate a
particular technique for model checking of NFSSs. Namely, in [58] the hash compaction technique is
described, which may cause a system state not to be explored with an unknown, but low, probability, thus
making probabilistic the final response itself.

Many methods have been proposed for probabilistic model checking, e.g. [4, 13, 16, 25–27, 44, 53, 62];
moreover, starting from the second half of the 90s, efficient Probabilistic Model Checkers [23, 28, 52] have
been developed. However, to the best of our knowledge, the currently state-of-the-art probabilistic model
checker is PRISM [2, 40, 52]. PRISM is a symbolic model checker, since it bases most of its computations
on MTBDDs (Multi Terminal Binary Decision Diagrams, [12]), which are a generalization of OBDDs
(Ordered Binary Decision Diagrams, [5]). Namely, OBDDs represents sets of states via their characteristic
function, while MTBDDs can represent the probability of a state to be in a set, by allowing real numbers
in the interval [0, 1] (instead of boolean values) on terminal nodes. More in detail, as it is shown in [40],
PRISM verification algorithm is based on an hybrid approach, which uses a MTBDD to represent the
system transition matrix and sparse matrix algorithms to carry out the (quantitative) probabilistic analysis.
This allows PRISM to be faster than probabilistic model checkers based only on MTBDDs (e.g., ProbVerus
[28]) and avoids the state explosion problem of probabilistic model checkers based only on sparse matrices
(e.g., ETMCC [31] or the algorithms in [25, 26]). A more detailed list of already available probabilistic
model checkers may be found in Section 1.1.1.

However, the general problem of probabilistic model checking has been proved to be P-SPACE complete
[42], so PRISM, as well as the other model checkers, is not able to solve all instances within a reasonable
time and/or memory. More specifically, as it happens in standard model checking of NFSSs, the main
obstruction is the state explosion problem, which arises, for PRISM, especially for systems requiring many
arithmetical operations on the state variables. In fact, it is known [5] that in this case OBDDs (and so
MTBDDs) reach their worst behavior, i.e. they require an exponential amount of memory w.r.t. the number
of variables describing the system. Examples of such systems are the so called Discrete Time Stochastic
Hybrid Systems, in which discrete and real variable coexist; for such systems, PRISM requires too much
memory to store the MTBDDs needed for the computation.

In standard model checking of NFSSs, this OBDD limitation can be sometimes overcome by using, in-
stead of a symbolic (i.e. OBDD-based) algorithm, an explicit one, i.e. based on an explicit enumeration
of the system states. This works especially for software like (asynchronous) systems [35], whereas sym-
bolic model checkers typically perform better on hardware like (synchronous) systems. Among the model
checkers based on symbolic algorithms, we have to cite SMV [56], NuSMV [48] and UPPAAL [43, 61],
while among the model checkers based on explicit algorithms we have to mention SPIN [57] and Murϕ
[47]. Thus, it is natural to think that an explicit algorithm may work also in probabilistic model check-
ing. However, to the best of our knowledge, the explicit approach has not been tried in probabilistic model
checking.

This motivated the development, the implementation and the experimentation of two explicit algorithms for
probabilistic model checking, which will be described in this Thesis:

• The first algorithm, while accepting a generic (Discrete Time) Markov Chain, only allows the verifi-
cation of finite horizon safety properties. Typically, this algorithm is able to determine if the proba-
bility that a system reaches, in a given number of steps, an error state, is less than a given probability
threshold. Thus, notwithstanding its limitation, this algorithm covers important properties.
The algorithm is described in Chapter 3, where we also give a proof of its correctness, together with
some experimental results comparing its performance with PRISM. These experiments show that the
1.1 Related Works 3

explicit approach to probabilistic model checking is indeed affordable, since our algorithm, as it was
expected, succeeds in verifying stochastic systems in which PRISM run out of a 2GB memory; these
systems indeed involve many arithmetic operations in the computing of the transition function. A
preliminary version of these results has already been published in [18].
• The second algorithm still takes as input a generic Markov Chain, but allows to verify general PCTL
formulas, provided that they only deal with system executions of finite length. Since this is equivalent
to have integer bounds on all the until operators of the PCTL formula (of course, different untils may
have different bounds), we call the resulting logic BPCTL (Bounded PCTL). In particular, this allows
to verify, besides the finite horizon safety properties of the first algorithm, also BPCTL formulas with
nested untils, e.g. the ones expressing reliability or robustness properties on system runs of finite
length. An example of such properties may be the sequent: “the probability of reaching within k 1
steps an undesired state, which will not be left with high probability within k 2 steps, is low”, where
k2 is low. By an undesired state we mean a state in which the system should not be, e.g. a state in
which the system cannot stay for a too long time, otherwise a damage occurs.
This algorithm, that is quite different from the first one, is described in Chapter 4, where we prove its
correctness and conduct some experiments, in order to compare its performances with PRISM. Again,
we have that, in the class of Markov Chains having a complex transition function, we outperform
PRISM. A preliminary version of these results has already been published in [20].

From the performance point of view, both algorithms are inspired by the results obtained in [21]. In fact,
they are both able to use magnetic disk storage in a clever way, i.e. limiting disk seeks. Because of the
large size of modern hard disks, we have that state explosion is hardly a problem for our algorithms, while
the computation time is our real bottleneck. Moreover, our algorithms use a cache in RAM (although in
different ways), allowing them to speed up computation by storing intermediate results. Thus, they can
trade RAM memory with computation time, i.e. the more RAM available the faster the verification.

Finally, both algorithms have been implemented within the Murϕ model checker; we call the resulting
probabilistic model checker FHP-Murϕ (Finite Horizon Probabilistic Murϕ, available at [24]).

As a concluding note, in Chapter 5 we analyze the properties of the new input language used by FHP-Murϕ
to define Markov Chains, by comparing it with the PRISM one. In this way, we will show that our language
is more natural than the PRISM one in order to specify many stochastic systems. A related idea has been
published in [19].

1.1 R ELATED W ORKS

Probabilistic model checking is a relatively young research field, the first works in this direction having
been published in the second half of the 90s. We can fix the starting point for probabilistic model checking
in [26], in which PCTL is introduced, together with an algorithm for PCTL model checking.

However, the algorithm in [26] was not feasible for large systems, since it was based on a state labeling. In
the following years, more effective algorithms have been proposed, extending the approach also to MDPs
and CTMCs, and basing the computation on MTBDDs; the more representative works in this direction
are [1, 2, 7, 37, 40]. Actually, the hybrid approach presented in [40] and implemented in PRISM may be
considered the best algorithm for probabilistic model checking at present.

1.1.1 P ROBABILISTIC M ODEL C HECKERS

We complete our brief survey of probabilistic model checking by giving a description of the probabilistic
model checkers which have been effectively developed.
4 Chapter 1. Introduction

PRISM (PRobabilistIc Symbolic Model checker[52] is, to the best of our knowledge, the currently state-
of-the-art probabilistic model checker, and it has been successfully used to verify some important
stochastic systems (e.g. [15, 41, 54]). PRISM can use three approaches to Markov Chain analysis:

• a sparse matrix based approach (option -s);


• a symbolic approach totally based on MTBDDs and OBDDs (option -m);
• a hybrid approach (default).

The meaning for these options is the following. As it is described in [40], the symbolic approach
performs well for memory occupation, but it is too slow in MTBDDs computations. However, it
turned out that numerical computations directly based on sparse matrices were orders of magnitude
faster. Thus, the hybrid approach was implemented, and is now the best verification modality. In
practice, the PRISM hybrid approach to probabilistic model checking uses extensions of the MTBDD
data structure and borrows ideas from the sparse matrix techniques in order to overcome performance
problems. By way of comparison, PRISM also allows to use directly the numerical sparse matrix
computational routines equivalent to the MTBDDs ones (this is the -s option). However, the hybrid
approach is the one giving the best results.
ProbVerus [28] is completely based on MTBDDs, so its computation engine is substantially equivalent
to PRISM with option -m. It works only with Discrete Time Markov Chains, but handles the full
PCTL. It is important for historical reasons, having been one of the first (succeeded) attempts to
apply symbolic algorithms to PCTL model checking. However, it is now covered by PRISM.
E ` M C 2 (ETMCC) [23] is a prototype model checker for CTMCs, where requirements are expressed in
CSL. Its verification engine is based on a sparse matrix representation, so it is substantially equivalent
to PRISM with option -s.
TwoTowers [60] is a software tool for the functional verification, security analysis, and performance eval-
uation of computer, communication and software systems. For the performance evaluation, TwoTow-
ers uses a Markov Chain solver which is based on numerical resolution [59], so it is equivalent to
sparse matrix techniques (PRISM option -s).

Finally, we have to mention APMC (Approximate Probabilistic Model Checker, [30]), which is not a prob-
abilistic model checker in the sense we use in this Thesis. In fact, APMC takes in input a Markov Chain M
and a positive LTL property φ (so, it does not accept probabilistic properties to be verified). Then, a ran-
domized distributed algorithm is used to approximate the probability that φ is satisfied with high confidence
by M.

1.2 T HESIS C ONTENT S UMMARY

Summing up, these are the Thesis contents:

Chapter 2 gives the basic definitions and properties which are necessary to understand the rest of the
Thesis.
Chapter 3 describes an explicit algorithm for the verification of finite horizon safety properties of Markov
Chains. The algorithm performances are then compared with PRISM ones.
Chapter 4 describes an explicit algorithm for the verification of BPCTL properties of Markov Chains.
Also this algorithm is then compared to PRISM.
Chapter 5 illustrates some interesting properties of the FHP-Murϕ input language.
C HAPTER 2

T HEORETICAL BASIS

In this chapter, we give some mathematical definitions and properties on Markov Chains and related con-
cepts, which will be useful in this Thesis. Moreover, in Section 2.4 we give a brief introduction to the model
checker Murϕ, since all our implementations are done within it.

Before going into the details of the mathematical statements we are interested in, we have to point out the
notation we use. To this end, let S be a finite set, the elements of which we call states. By assuming that
there is a predefined order on states, and by abuse of notation, we will indifferently speak of states and
natural number indices, so considering n + 1 as denoting the state s if n is the number of states preceding
s in the given order.

This allows us to use states as row vectors (and matrices) indices. Thus, if S = {s 1 , . . . , sn } and x ∈ Rn ,
then for all 1 ≤ i ≤ n we pose xsi = xi , and analogously, for P ∈ Rn×n , we pose Psi ,sj = Pi,j for all
1 ≤ i, j ≤ n. Note that this also allows to regard a function f from S to [0, 1] as a |S|-dimensional row
vector x: it is sufficient to pose xi = f (si ). Analogously, a function f from S × S to [0, 1] is regarded as a
|S| × |S|-dimensional matrix P, by posing Pi,j = f (si , sj ).

Example 2.1. If S = {0, 5, 7} (the order is the straightforward


  and f : S → [0, 1] is such that
one),
i+1 1
f (i) = 10 , then f is represented by the vector x = 10 1 6 8 .

i+j
Analogously, if f : S × S → [0, 1] is such that f (i, j) = 20 , then f is represented by the matrix

 
0 5 7
1 
P=  5 10 12  .

20
7 12 14

This allows us to give our first definition.

Xfollowing) on S is a function x : S →
Definition 2.1. A probability distribution (simply distribution in the
[0, 1], or equivalently a |S|-dimensional row vector x, such that x(i) = 1. A distribution x represents
i∈S
state j ∈ S iff x(j) = xj = 1 (thus x(i) = xi = 0 when i 6= j).

If distribution x represents s ∈ S, by abuse of language we also write x ∈ S to mean that distribution


x represents a state and we use x in place of s, i.e. of the element of S represented by x. In the follow-
ing we often represent states using distributions, since this allows us to use matrix notation to define our
computations.

For exposition completeness


P we recall the standard matrix operations: xP is the row vectorP
y such that, for
all s ∈ S, ys = j∈S xj Pj,s and AB is the matrix C such that, for all s, t ∈ S, Cs,t = j∈S As,j Bj,t .

5
6 Chapter 2. Theoretical Basis

Moreover, An , being n ∈ N, is defined in the usual way, i.e.: A0 = I, An+1 = An A, where I is the
identity matrix such that, for all s ∈ S, I(s, s) = 1 and I(s, t) = 0 for s 6= t.

We also recall some notations and definitions from other mathematical fields. We denote with B the set
{0, 1} of boolean values; as usual 0 stands for false and 1 stands for true. Moreover, we denote with S ∗ the
set of all finite sequences of symbols in S, while S ∞ is the set of all infinite sequences of symbols in S.

Finally, the following Definition, taken from [55], will be useful in the following.
Definition 2.2. Let X be a set. Then a σ-algebra F is a nonempty collection of subsets of X such that the
following holds:

1. The empty set is in F .


2. If A is in F , then so is the complement of A.
3. If An is a sequence of elements of F , then the union of the An s is in F .

If S is any collection of subsets of X, then we can always find a σ-algebra containing S, namely the power
set of X. By taking the intersection of all σ-algebras containing S, we obtain the smallest such σ-algebra.
We call the smallest σ-algebra containing S the σ-algebra generated by S.

2.1 M ARKOV C HAINS

In this section, we give some basic definitions on Markov Chains; for further details see, e.g., [3].
Definition 2.3. Let S be a finite set of states. A Discrete Time Markov Chain (just Markov Chain in the
following) is a triple M = X(S, P, q) where q ∈ S is the initial state and P : S × S → [0, 1] is a stochastic
matrix, i.e. for all s ∈ S, P(s, t) = 1.
t∈S

Definition 2.4. An execution sequence (or path) in the Markov Chain M = (S, P, q) is a nonempty finite
or infinite sequence π = s0 s1 s2 . . . where, for all i ≥ 0, si ∈ S is a state and P(si , si+1 ) > 0. If π is
a path, we write π(k) for the state sk , and we write π|k for the sequence s0 s1 s2 . . . sk−1 . The length of a
finite path π = s0 s1 s2 . . . sk is k, i.e. the number of transitions, whereas the length of an infinite path is
∞; we denote with |π| the length of π. In M we also define the following sets of paths:

• Path(M, s) = {π ∈ S ∞ | π is a path in M and π(0) = s} is the set of infinite paths π in M


starting from s;
• Pathk (M, s) = {π ∈ S ∗ | π is a path in M and π(0) = s and |π| = k} is set of paths π in M
starting from s and of length exactly k;
• Path≤k (M, s) = {π ∈ S ∗ | π is a path in M and π(0) = s and |π| ≤ k} is set of paths π in M
starting from s and of length at most k;
• Path(M) = Path(M, q);
• Pathk (M) = Pathk (M, q);
• PathM = {π ∈ Path(M, s) | s ∈ S} is the set of all infinite paths in M.

Finally, we say that a state s ∈ S is reachable in k steps if it exists a path π ∈ Path k (M) such that
π(k) = s.
2.1 Markov Chains 7

X
Definition 2.5. Let M = (S, P, q) be a Markov Chain. For s ∈ S we denote with (s) the smallest
σ-algebra on Path(M, s) which, for any finite path ρ starting at s, contains P the basic cylinders {π ∈
Path(M, s) | ρ is a prefix of π}. Then, the probability measure Prob on (s) is the unique measure
such that, for any finite path ρ starting at s, Prob{π ∈ Path(M, s) | ρ is a prefix of π} = P(ρ) =
k−1
Y
P(ρ(i), ρ(i + 1)) = P(ρ(0), ρ(1)) P(ρ(1), ρ(2)) · · · P(ρ(k − 1), ρ(k)), where k = |ρ|.
i=0

A Markov Chain M = (S, P, q) can be seen as a method to define what is the probability p to go to a state
t ∈ S, when the current state is s ∈ S and it is not required that p depends on the preceding path leading
from q to s; in this case, we will have p = P(s, t).

Example 2.2. Let S = {s0 , s1 } and M = (S, P, s0 ), with

!
0 1
P= 1 2
.
3 3

Then, if the current state is s0 , the next state will be s1 . On the contrary, if the current state is s1 , then there
is probability 13 to go to state s0 and probability 32 to stay in state s1 . Moreover, being P(s0 , s0 ) = 0, we
have that Path(M) = {π ∈ S ∞ | π(0) = s0 and π(k) = s0 ⇒ π(k + 1) = s1 }.

A graphical representation of M is shown in Figure 2.1.

2
0 s0 s1 3

1
3

Figure 2.1: Graphical representation of the Markov Chain M of Example 2.2

Definition 2.4 shows that a Markov Chain M = (S, P, q) can be viewed as a method to generate sequences
of states. More generally, M can generate a sequence
 of distributions. In fact, let M be the Markov Chain
defined in Example 2.2, and let x = 1 0 be the distribution representing s0 . Then, we have that
 
xP = 0 1 = s1 , and in fact, starting from s0 , the only possibility is to go to state s1 . On the
   
1 2
contrary, if x = 0 1 = s1 , then we have that xP = 3 3
, which is the right probability
distribution starting from s1 .

Thus, given a distribution x, the distribution y obtained by one execution step of Markov Chain M =
(S, P, q) is computed as: y = xP. In particular if y = xP and x represents state s we have that, for all
t ∈ S, y(t) = Ps,t , so the distribution obtained by making one step from s coincides with the s-th row of
P.
8 Chapter 2. Theoretical Basis

2.1.1 “R EAL W ORLD ” M ARKOV C HAINS

The Markov Chain definition given in Definition 2.3 is appropriate to study mathematical properties of
Markov Chains. However, Markov Chains arising from probabilistic concurrent systems are usually defined
using a suitable programming language rather than a stochastic matrix. As a matter of fact the (huge) size
of the stochastic matrix of concurrent systems is one of the main obstructions to overcome in probabilistic
model checking.

Thus, a Markov Chain is presented to a probabilistic model checker by defining, using a suitable program-
ming language, a next state function that returns the immediate successors of a given state, together with
the probability of the corresponding transition. The following definition formalizes this notion.

Definition 2.6. A Probabilistic Finite State System (shortened PFSS in the following) S is a 4-tuple
(S, q, A, next), where S is a finite set of states, q ∈ S is the initial state, A is a finite set of labels
and next : S → 2S×A×[0,1] is a function taking X a state s as argument and returning a set next(s) of
triplets (t, a, p) ∈ S × A × [0, 1] such that p = 1.
(t,a,p)∈next(s)

Note that, in Definition 2.6, we are forced to introduce the set A of labels in order to manage the case in
which the next function returns more than one probabilistic transition from a state s to the same state t
with the same probability p. The following example should clarify this point.

Example 2.3. Consider the simple FHP-Murϕ code (see Section 2.4.1 and Appendix A.1) in Figure 2.2;
the corresponding PFSS is S = ({s0 , s1 }, s0 , {r1, r2, r3, r4}, next), where function next is defined as:
next(s0 ) = {(s0 , r1, 1)} and next(s1 ) = {(s0 , r2, 61 ), (s0 , r3, 61 ), (s1 , r4, 32 )}. In S, we denoted with si
the state in which x has value i, and we used the rule names as labels. Note that, since there are two rules
leading from s1 to s0 with probability 16 , they have to be distinguished by different labels (r2 and r3) in
the definition of next.

A graphical representation of S is shown in Figure 2.3.

Between PFSS and Markov Chains there is a strict connection. Namely, with each PFSS can be associated
exactly one Markov Chain (see Definition 2.7). On the other hand, there are many PFSSs describing a given
Markov Chain (see Section 5.1).
mc
Definition 2.7. Let S = (S, q, A, next) be a PFSS. X The Markov Chain associated with S is S =
(S, P, q), where, for all s, t ∈ S, P(s, t) = p.
(t,a,p)∈next(s)

Moreover, a state sequence π = s0 s1 s2 . . . is a path in S iff it is a path in S mc ; this allows to define also
Path(S, s) = Path(S mc , s) for all s ∈ S, and Path(S) = Path(S, q).

Example 2.4. If S is the PFSS given in Example 2.3, then S mc equals the M of Example 2.2.

2.2 T HE BPCTL L ANGUAGE

In this section we give syntax (Definition 2.8) and semantics (Definition 2.9) for the language we use to
define the formulas. Namely, we define syntax and semantics for the BPCTL (Bounded PCTL) language.
The BPCTL language is a proper subset of the PCTL language (Probabilistic Computational Tree Logic
[26]) in which all Until operators are bounded, possibly with different bounds. This entails that we consider
only paths (system runs) of bounded length.
2.2 The BPCTL Language 9

var x : 0..1; /* x can assume values 0 and 1 */

/* Auxiliary function returning the real value 1.0 if the


argument x is true, and 0.0 otherwise */
f u n c t i o n FromBoolToReal(x : b o o l e a n) : r e a l ;
begin
r e t u r n x ? 1.0 : 0.0;
end;

/* Definition of the initial state */


s t a r t s t a t e "init"
begin
x := 0;
end;

/* If we are in 0, we surely move to 1 */


r u l e "r1"
FromBoolToReal(x = 0) ==>
begin
x := 1;
end;

1
/* If we are in 1, we move to 0 with probability 6 */
r u l e "r2"
(1.0/6.0)*FromBoolToReal(x = 1) ==>
begin
x := 0;
end;

1
/* Again, if we are in 1, we move to 0 with probability 6 */
r u l e "r3"
(1.0/6.0)*FromBoolToReal(x = 1) ==>
begin
x := 0;
end;

2
/* If we are in 1, we stay in 1 with probability 3 */
r u l e "r4"
(2.0/3.0)*FromBoolToReal(x = 1) ==>
begin
x := 1;
end;

Figure 2.2: A simple FHP-Murϕ code defining the PFSS of Example 2.3
10 Chapter 2. Theoretical Basis

r1, 1

r2, 61

s0 s1 r4, 32

r3, 61

Figure 2.3: Graphical representation of the PFSS S of Example 2.3

Definition 2.8. Let AP be a finite set of atomic propositions on a finite set of states S, i.e. of boolean
functions p : S → B. The BPCTL language LBP CT L is the language generated by the following grammar:

Φ ::= tt | p | Φ1 ∧ Φ2 | ¬Φ | [XΦ]wα | [Φ1 U≤k Φ2 ]wα

where α ∈ [0, 1], p ∈ AP , k ∈ N and the symbol w is one of the symbols >, ≥.
Definition 2.9. Let M = (S, P, q) be a Markov Chain. Then, the satisfaction relation |= ⊆ S × L BP CT L
is defined, for all s ∈ S, as follows (to shorten our notation, we write s |= Φ to mean that (s, Φ) ∈|=):

• s |= tt (tt stands for true, so each state satisfies it);


• s |= p iff p(s) = 1;
• s |= Φ1 ∧ Φ2 iff s |= Φ1 and s |= Φ2 ;
• s |= ¬Φ iff s 6|= Φ (i.e., iff s |= Φ does not hold);
• s |= [XΦ]wα iff Prob{π ∈ Path(M, s) | π |= XΦ} w α, where

– π |= XΦ iff π(1) |= Φ;

• s |= [Φ1 U≤k Φ2 ]wα iff Prob{π ∈ Path(M, s) | π |= Φ1 U≤k Φ2 } w α, where

– π |= Φ1 U≤k Φ2 iff ∃h ≤ k : (π(h) |= Φ2 and ∀i < h π(i) |= Φ1 ).

Moreover, let F be a BPCTL formula. Then, M |= F iff q |= F .

Finally, let S be a PFSS. Then, S |= F iff S mc |= F .

The two following definitions will be useful in the following.


Definition 2.10. A BPCTL formula Φ is said to be a U-formula iff there are two BPCTL formulas Φ 1 , Φ2 ∈
LBP CT L , k ∈ N and α ∈ [0, 1] such that Φ ≡ [Φ1 U≤k Φ2 ]wα . Analogously, Φ is said to be a X-formula
iff there are a BPCTL formula Φ1 ∈ LBP CT L and α ∈ [0, 1] such that Φ ≡ [X Φ1 ]wα .
Definition 2.11. Let M = (S, P, q) be a Markov Chain, Φ, Ψ ∈ LBP CT L be BPCTL formulas, k ∈ N
and s ∈ S. Then we write Ps [Φ U≤k Ψ] for Prob{π ∈ Path(M, s) | π |= Φ U≤k Ψ}, and Ps [X Φ] for
Prob{π ∈ Path(M, s) | π |= X Φ}.

With overload of notation, if F ≡ [Φ U≤k Ψ]wα is a U-formula, we write Ps [F ] for Prob{π ∈


Path(M, s) | π |= Φ U≤k Ψ}. Analogously, if F ≡ [X Φ]wα is a X-formula, we write Ps [F ] for
Prob{π ∈ Path(M, s) | π |= X Φ}.
2.2 The BPCTL Language 11

2.2.1 BPCTL B OUNDED S EMANTICS

From Definition 2.9, we can intuitively see that the truth value for s |= Φ can be evaluated by taking
into account only paths of finite length k, provided that k is largeX
enough and computing, when needed,
Prob{π ∈ Pathk (M, s) | P(π)} (for some path property P) as P(π).
π | P(π)

This is formally stated in Theorem 2.4: for all BPCTL formulas Φ, there exists a k ∈ N such that, for all
states s ∈ S and for all h ≥ k, s |=h Φ iff s |= Φ. This Section is dedicated to the proof of Theorem 2.4.

First of all, we have to give the semantics of |=k (Definition 2.12), which will be the same as |=, but will be
undefined when it is necessary to take into consideration paths of length greater than k. Recall that 0 stands
for false and 1 stands for true.
Definition 2.12. Let M = (S, P, q) be a Markov Chain. Then, the satisfaction function |=: S × N ×
LBP CT L → {⊥, 0, 1} is defined, for all s ∈ S, k ∈ N and Φ, Φ1 , Φ2 ∈ LBP CT L , as follows (to shorten
our notation, we write s |=k Φ for |= (s, k, Φ)):

• s |=k tt = 1
• s |=k p = p(s)
(
⊥ if s |=k Φ =⊥
• s |=k ¬Φ =
not s |=k Φ otherwise
(
⊥ if s |=k Φ1 =⊥ or s |=k Φ2 =⊥
• s |=k Φ1 ∧ Φ2 =
s |=k Φ1 and s |=k Φ2 otherwise
(
⊥ if A
• s |=k [XΦ]wα =
Prob{π ∈ Path(M, s) | π |=k XΦ = 1} w α otherwise
(
≤h ⊥ if B
• s |=k [Φ1 U Φ2 ]wα =
Prob{π ∈ Path(M, s) | π |=k Φ1 U≤h Φ2 = 1} w α otherwise

where we pose, to make the last two formulas more readable, A ≡ ∃π ∈ Path(M, s) : π |= k XΦ =⊥ and
B ≡ ∃π ∈ Path(M, s) : π |=k Φ1 U≤h Φ2 =⊥.

By simultaneous induction hypothesis the satisfaction function |=: Path M × N × LBP CT L → {1, 0, ⊥}, is
given in the following (again, π |=k Φ =def |= (π, k, Φ)). Let π ∈ PathM and k ∈ N, then:

(
⊥ if k < 1
• π |=k XΦ =
π(1) |=k−1 Φ otherwise


 ⊥ if C
• π |=k Φ1 U≤h Φ2 = ∃j ∈ N : 0 ≤ j ≤ h and π(j) |=k−j Φ2

and (∀0 ≤ i ≤ j − 1 π(i) |=k−i Φ1 ) otherwise

where we pose, to make the last formula more readable, C ≡ (k < h) or (∃j ∈ N : 0 ≤ j ≤
h and π(j) |=k−j Φ2 =⊥) or (∃j ∈ N : 0 ≤ j ≤ h − 1 and π(j) |=k−j Φ1 =⊥).

Finally, let Φ ∈ LBP CT L be a BPCTL formula. Then we put M |=k Φ =def q |=k Φ.
12 Chapter 2. Theoretical Basis

Note that |=k is defined in a strict way, since π |=k Φ is sometimes set to ⊥ even when it could be evaluated.
For instance, suppose that we have to evaluate π |=1 Φ, with Φ ≡ [[φ1 U≤2 φ2 ]≥0.5 U≤1 φ3 ]≥0.6 , being φ1 ,
φ2 and φ3 atomic propositions; then, by Definition 2.12, π |=1 Φ =⊥. However, if π(0) and all the states
which are reachable from π(0) do not satisfy φ1 , then π |=1 Φ could be evaluated. In fact, since s 6|= φ1
implies Ps [φ1 U≤2 φ2 ] = 0 6≥ 0.5, it is not necessary to examine, for all states s which are reachable in 1
step from π(0), the paths of length 2 starting from s. We make this choice in Definition 2.12 because in this
section we are interested in the theoretical fact that exists a k such that s |= k Φ iff s |= Φ, and not in finding
the minimum of such k; moreover, we want this k to depend only on Φ, and not on the PFSS or Markov
Chain under analysis. Of course, in the algorithm of Chapter 4 we consider also the performance aspect.

Definition 2.12 directly implies the following Lemma.


Lemma 2.1. Let M = (S, P, q) be a Markov Chain and Φ ∈ LBP CT L be a BPCTL formula. Then, for all
k ∈ N, we have that (∀s ∈ S s |=k Φ 6=⊥) or (∀s ∈ S s |=k Φ =⊥).

Proof. The proof is by structural induction on Φ: supposing that ∀k ∈ N ((∀s ∈ S s |= k Φi 6=⊥) or (∀s ∈
S s |=k Φi =⊥)) holds for all subformulas Φi of Φ, we show that ∀k ∈ N ((∀s ∈ S s |=k Φ 6=⊥) or
(∀s ∈ S s |=k Φ =⊥).

As induction basis note that, if Φ is tt or an atomic proposition (i.e., it does not contain any subformula),
then s |=k Φ 6=⊥ for all k ∈ N and s ∈ S, so the induction basis is proved. For the other cases, we have:

1. if Φ ≡ ¬Φ1 , then, by Definition 2.12, we have that, for all k ∈ N and s ∈ S, s |= k Φ1 =⊥ iff
s |=k Φ =⊥, so the induction step is proved;
2. if Φ ≡ Φ1 ∧ Φ2 , then fix a k ∈ N. By induction hypothesis, we have the following cases:

(a) ∀s ∈ S s |=k Φ1 =⊥ and ∀s ∈ S s |=k Φ2 =⊥, so ∀s ∈ S s |=k Φ =⊥ and the induction step
is proved;
(b) ∀s ∈ S s |=k Φ1 6=⊥ and ∀s ∈ S s |=k Φ2 =⊥, so ∀s ∈ S s |=k Φ =⊥ and the induction step
is proved;
(c) ∀s ∈ S s |=k Φ1 =⊥ and ∀s ∈ S s |=k Φ2 6=⊥: as the previous case;
(d) ∀s ∈ S s |=k Φ1 6=⊥ and ∀s ∈ S s |=k Φ2 6=⊥, so ∀s ∈ S s |=k Φ 6=⊥ and the induction step
is proved;

3. if Φ ≡ [XΦ1 ]wα , then fix a k ∈ N. By induction hypothesis, we have the following cases:

(a) ∀s ∈ S s |=k Φ1 =⊥, so ∀s ∈ S s |=k Φ =⊥ and the induction step is proved;


(b) ∀s ∈ S s |=k Φ1 6=⊥, then, again by induction hypothesis, there are the following cases:
i. k < 1, so ∀s ∈ S s |=k Φ =⊥ and the induction step is proved;
ii. ∀s ∈ S s |=k−1 Φ1 6=⊥, so ∀s ∈ S s |=k Φ 6=⊥ and the induction step is proved;
iii. ∀s ∈ S s |=k−1 Φ1 =⊥, so ∀s ∈ S s |=k Φ =⊥ and the induction step is proved;

4. if Φ ≡ [Φ1 U≤h Φ2 ]wα , then fix a k ∈ N. By induction hypothesis, we have the following cases:

(a) ∀s ∈ S s |=k Φ1 =⊥ and ∀s ∈ S s |=k Φ2 =⊥, so ∀s ∈ S s |=k Φ =⊥ and the induction step
is proved;
(b) ∀s ∈ S s |=k Φ1 6=⊥ and ∀s ∈ S s |=k Φ2 =⊥, so ∀s ∈ S s |=k Φ =⊥ and the induction step
is proved;
(c) ∀s ∈ S s |=k Φ1 =⊥ and ∀s ∈ S s |=k Φ2 6=⊥: as the previous case;
(d) ∀s ∈ S s |=k Φ1 6=⊥ and ∀s ∈ S s |=k Φ2 6=⊥, then, again by induction hypothesis, there are
the following cases:
2.2 The BPCTL Language 13

i. k < h, so ∀s ∈ S s |=k Φ =⊥ and the induction step is proved;


ii. ∃0 ≤ j ≤ h : ∀s ∈ S s |=k−j Φ2 =⊥, so ∀s ∈ S s |=k Φ =⊥ and the induction step is
proved;
iii. ∃0 ≤ j ≤ h − 1 : ∀s ∈ S s |=k−j Φ1 =⊥, so ∀s ∈ S s |=k Φ =⊥ and the induction step
is proved;
iv. all the above three cases are false, so ∀s ∈ S s |=k Φ 6=⊥ and the induction step is proved.

Now, we want to compute a horizon k ∈ N such that, for all Φ ∈ LBP CT L , h ≥ k and s ∈ S, s |=h Φ iff
s |= Φ. This k is given as a function of the BPCTL formula in Definition 2.13.
Definition 2.13. The function µ : LBP CT L → N is inductively defined as follows:

• µ(tt) = 0
• µ(p) = 0 for all atomic propositions p ∈ AP
• µ(¬Φ) = µ(Φ)
• µ(Φ1 ∧ Φ2 ) = max{µ(Φ1 ), µ(Φ2 )}
• µ([XΦ]wα ) = µ(Φ) + 1
(
max{µ(Φ1 ) + h − 1, µ(Φ2 ) + h} if h ≥ 1
• µ([Φ1 U≤h Φ2 ]wα ) =
µ(Φ2 ) otherwise

We now demonstrate (Theorem 2.4) that the value returned by µ has the required properties. To do this, we
start with two lemmas.
Lemma 2.2. Let M = (S, P, q) be a Markov Chain and Φ ∈ LBP CT L be a BPCTL formula. Then, if
there exists a k ∈ N such that, for all states s ∈ S, s |=k Φ 6=⊥, then we have that, for all n ≥ k and for
all states s ∈ S, s |=n Φ 6=⊥.

Proof. As for Lemma 2.1, the proof is by structural induction on Φ: supposing that ∀k ∈ N ((∀s ∈ S s |= k
Φi 6=⊥) ⇒ (∀n ≥ k ∀s ∈ S s |=n Φi 6=⊥)) holds for all subformulas Φi of Φ, we show that
∀k ∈ N ((∀s ∈ S s |=k Φ 6=⊥) ⇒ (∀n ≥ k ∀s ∈ S s |=n Φ 6=⊥)).

As induction basis note that, if Φ is tt or an atomic proposition (i.e., it does not contain any subformula),
then s |=k Φ 6=⊥ for all k ∈ N and s ∈ S, so the induction basis is proved. For the other cases, we have:

1. if Φ ≡ ¬Φ1 , then, by Definition 2.12, we have that, for all k ∈ N and s ∈ S, s |=k Φ1 =⊥ iff
s |=k Φ =⊥, so the induction step is proved;
2. if Φ ≡ Φ1 ∧ Φ2 , then we have the following cases:

(a) ∀k ∈ N ∃s ∈ S : s |=k Φ =⊥, and the induction step is proved (however note that, by Lemma
2.1, this implies that ∀k ∈ N ∀s ∈ S s |=k Φ =⊥; by Lemma 2.3, this is impossible);
(b) otherwise, let k ∈ N be such that ∀s ∈ S s |=k Φ 6=⊥. Then, by Definition 2.12, we have that
∀s ∈ S s |=k Φ1 6=⊥ and ∀s ∈ S s |=k Φ2 6=⊥. By induction hypothesis, we have that ∀n ≥
k ∀s ∈ S s |=n Φ1 6=⊥ and ∀n ≥ k ∀s ∈ S s |=n Φ2 6=⊥, so ∀n ≥ k ∀s ∈ S s |=n Φ 6=⊥
and the induction step is proved;
14 Chapter 2. Theoretical Basis

3. if Φ ≡ [XΦ1 ]wα , then we have the following cases:

(a) ∀k ∈ N ∃s ∈ S : s |=k Φ =⊥ and the induction step is proved (however, it holds the same
reasoning of item 2a);
(b) otherwise, let k ∈ N be such that ∀s ∈ S s |=k Φ 6=⊥. Then, by Definition 2.12, we have that
k ≥ 1 and ∀π ∈ Path(M, s) π(1) |=k−1 Φ1 6=⊥; so, by Lemma 2.1, ∀s ∈ S s |=k−1 Φ1 6=⊥.
By induction hypothesis, we have that ∀n ≥ k−1 ∀s ∈ S s |=n Φ1 6=⊥, so, again by Definition
2.12, ∀n ≥ k ∀s ∈ S s |=n Φ 6=⊥ and the induction step is proved;

4. if Φ ≡ [Φ1 U≤h Φ2 ]wα , then we have the following cases:

(a) ∀k ∈ N ∃s ∈ S : s |=k Φ =⊥, and the induction step is proved (however, it holds the same
reasoning of item 2a);
(b) otherwise, let k ∈ N be such that ∀s ∈ S s |=k Φ 6=⊥. Then, by Definition 2.12, we have that
k ≥ h and ∀j = 0, . . . , h ∀s ∈ S s |=k−j Φ2 6=⊥ and ∀j = 0, . . . , h − 1 ∀s ∈ S s |=k−j
Φ1 6=⊥. By induction hypothesis, we have that ∀n ≥ k − h ∀s ∈ S s |=n Φ2 6=⊥ and
∀n ≥ k − h + 1 ∀s ∈ S s |=n Φ1 6=⊥. Hence, ∀n ≥ k ∀s ∈ S s |=n Φ 6=⊥ and the induction
step is proved.

Lemma 2.3. Let M = (S, P, q) be a Markov Chain and Φ ∈ LBP CT L be a BPCTL formula. Then, for all
k ≥ µ(Φ), we have that, for all s ∈ S, s |=k Φ 6=⊥.

Proof. By Lemma 2.2, it is sufficient to prove that ∀s ∈ S s |=µ(Φ) Φ 6=⊥.

As for Lemmas 2.1 and 2.2, the proof is by structural induction on Φ: supposing that ∀s ∈ S s |= µ(Φi )
Φi 6=⊥ holds for all subformulas Φi of Φ, we show that ∀s ∈ S s |=µ(Φ) Φ 6=⊥.

As induction basis, we have that if Φ is tt or an atomic proposition (i.e., it does not contain any subformula),
then it is never evaluated to ⊥. For the other cases, we have:

1. if Φ ≡ ¬Φ1 , then by structural induction we know that, ∀s ∈ S s |=µ(Φ1 ) Φ1 6=⊥. Since µ(Φ) =
µ(Φ1 ), and by Definition 2.12, for all k ∈ N and s ∈ S, s |=k Φ1 6=⊥ iff s |=k Φ 6=⊥, then we have
that for all s ∈ S, s |=µ(Φ) Φ 6=⊥, and the induction step is proved;
2. if Φ ≡ Φ1 ∧ Φ2 , then by induction hypothesis we know that, ∀s ∈ S s |=µ(Φ1 ) Φ1 6=⊥6= s |=µ(Φ2 )
Φ2 . Since µ(Φ) = max{µ(Φ1 ), µ(Φ2 )}, we have that µ(Φ) ≥ µ(Φ1 ) and µ(Φ) ≥ µ(Φ2 ), so by
Lemma 2.2 ∀s ∈ S s |=µ(Φ) Φ1 6=⊥6= s |=µ(Φ) Φ2 . By Definition 2.12, we have that ∀s ∈
S s |=µ(Φ) Φ 6=⊥;
3. if Φ ≡ [XΦ1 ]wα , then by induction hypothesis we know that, ∀s ∈ S s |=µ(Φ1 ) Φ1 6=⊥. By
Definition 2.12, this statement, together with the fact that µ(Φ1 ) + 1 ≥ 1 (this holds since, for all
Ψ ∈ LBP CT L , µ(Φ) ≥ 0), implies that ∀s ∈ S s |=µ(Φ1 )+1 Φ 6=⊥. Since µ(Φ) = µ(Φ1 ) + 1, we
have again that s |=µ(Φ) Φ 6=⊥;

4. if Φ = [Φ1 U≤h Φ2 ]wα , then by induction hypothesis we know that, ∀s ∈ S s |=µ(Φ1 ) Φ1 6=⊥6=
s |=µ(Φ2 ) Φ2 . Now, if h = 0, by Definition 2.12 we have that ∀k ∈ N ∀s ∈ S s |=k Φ 6=⊥ iff
s |=k Φ2 6=⊥, so, being µ(Φ) = µ(Φ2 ), the induction step is proved. Otherwise, if h ≥ 1 then
µ(Φ) = max{µ(Φ1 ) + h − 1, µ(Φ2 ) + h}. Suppose, by contradiction, that ∃s ∈ S : s |=µ(Φ) Φ =⊥.
By Definition 2.12, at least one of these three conditions has to hold:

(a) µ(Φ) < h: this is impossible since, being µ(Ψ) ≥ 0 for all Ψ ∈ LBP CT L , we have that
µ(Φ) = max{µ(Φ1 ) + h − 1, µ(Φ2 ) + h} ≥ max{h − 1, h} = h;
2.3 Finite State/Discrete Time Stochastic Processes 15

(b) there exists a π ∈ Path(M, s) such that ∃j ∈ N : 0 ≤ j ≤ h and π(j) |=µ(Φ)−j Φ2 =⊥.
However, by induction basis we know that π(j) |=µ(Φ2 )Φ2 6=⊥; moreover, µ(Φ) ≥ µ(Φ2 ) + h,
so, for all j ≤ h, µ(Φ) − j ≥ µ(Φ2 ). These two facts imply, by Lemma 2.2 and induction
hypothesis, that π(j) |=µ(Φ)−j Φ2 6=⊥;
(c) there exists a π ∈ Path(M, s) such that ∃j ∈ N : 0 ≤ j ≤ h − 1 and π(j) |=µ(Φ)−j Φ1 =⊥.
However, by induction basis we know that π(j) |=µ(Φ1 )Φ1 6=⊥; moreover, µ(Φ) ≥ µ(Φ1 ) + h −
1, so, for all j ≤ h − 1, µ(Φ) − j ≥ µ(Φ1 ). These two facts imply, by Lemma 2.2 and induction
hypothesis, that π(j) |=µ(Φ)−j Φ1 6=⊥.

Now, we can give our main Theorem.

Theorem 2.4. Let M = (S, P, q) be a Markov Chain and Φ ∈ LBP CT L be a BPCTL formula. Then, for
all k ≥ µ(Φ) and s ∈ S, we have that s |=k Φ iff s |= Φ.

Proof. The proof descends immediately from Lemma 2.3 and from the fact that, for all Φ ∈ L BP CT L ,
k ∈ N and s ∈ S, s |=k Φ 6=⊥ implies s |=k Φ = s |= Φ; the latter statement holds because Definitions
2.9 and 2.12 are equal every time that the latter does not evaluate to ⊥.

Finally, we give a simple corollary of Theorem 2.4.

Corollary 2.5. Let M = (S, P, q) be a Markov Chain and Φ ∈ LBP CT L be a BPCTL formula. Then
M |=µ(Φ) Φ iff M |= Φ.

2.3 F INITE S TATE /D ISCRETE T IME S TOCHASTIC P ROCESSES

In this section, we give some basic definitions on Finite State/Discrete Time General Stochastic Processes;
for more details on stochastic processes see, e.g., [49].

Definition 2.14. Let S be a finite set of states. A Finite State/Discrete Time Stochastic Process (shortened
SP in the following) is a triple X = (S, P, q) where q ∈ S is the Xinitial state and the transition function
∗ ∗
P : S × S × S → [0, 1] is such that for all s ∈ S and π ∈ S , P(s, π, t) = 1.
t∈S

Definition 2.15. An execution sequence (or path) in the SP X = (S, P, q) is a nonempty (finite or infinite)
sequence π = s0 s1 s2 . . . where si are states and

P(si , s0 . . . si−1 , si+1 ) > 0

for i ≥ 0. If π = s0 s1 s2 . . . we write π(k) for sk , and we write π|k for the sequence s0 s1 s2 . . . sk−1 . The
length of a finite path π = s0 s1 s2 . . . sk is k, i.e. the number of transitions, whereas the length of an infinite
path is ∞; we denote with |π| the length of π. We denote with Path(X , s) the set of infinite paths π in X
such that π(0) = s; finally, we write also Path(X ) for Path(X , q).
16 Chapter 2. Theoretical Basis

P
Definition 2.16. Let X = (S, P, q) be a SP. For s ∈ S we denote with (s) the smallest σ-
the basic cylinders {π ∈
algebra on Path(X , s) which, for any finite path ρ starting at s, contains P
Path(X , s) | ρ is a prefix of π}. The probability measure Prob on (s) is the unique mea-
k−1
Y
sure with Prob{π ∈ Path(X , s) | ρ is a prefix of π} = P(ρ) = P(ρ(i), ρ|i, ρ(i + 1)) =
i=0
P(ρ(0), , ρ(1))P(ρ(1), ρ|1, ρ(2)) · · · P(ρ(k − 1), ρ|(k − 1), ρ(k)), where k = |ρ| and  is the empty se-
quence.

By Definition 2.3, it is clear that a Markov Chain is a particular SP, such that the transition function
P(s, π, t) actually does not depend on π (“lack of memory”) and therefore reduces to a stochastic matrix.

Example 2.5. Let S = {s0 , s1 } and X = (S, P, s0 ), with (remember that  is the empty sequence of
states):

 1

 2 if s = s0 and t = s0 and π =
1

if s = s0 and t = s1 and π =




 2
 1 if s = s0 and t = s0 and π 6=  and ∀0 ≤ k < |π| π(k) = s0
P(s, π, t) =


 1 if s = s0 and t = s1 and π 6=  and ∃0 ≤ k < |π| : π(k) = s1
1 s = s1 and t = s1



 if

0 otherwise

Here we have that Path(X ) = {π ∈ S ∞ | π(0) = s0 and (∀k ≥ 1 π(k) = s0 or ∀k ≥ 1 π(k) = s1 )} =


{s∞ ∞
0 , s0 s1 }.

Now, we want to verify BPCTL properties not only on Markov Chains, but also on SPs. However, this
objective can be very difficult to reach, both from a computational and from an analytical point of view,
also on very simple properties such as the safety ones [4, 13, 14]. So, the first task is to single out a large
enough class of tractable SP. So, we restrict our analysis to SP such that their transition probabilities depend
only on some fixed characteristics of the process history. We formalize this as follows.

Definition 2.17. Let X = (S, P, q) be an SP. We say that X has finite character n iff there exists an
equivalence relation R on S ∗ of finite index n (that is with n equivalence classes) such that for every π 1 ,
π2 ∈ S ∗

if R(π1 , π2 ) then ∀s, t ∈ S P(s, π1 , t) = P(s, π2 , t)

We will show (Chapter 5) that finite character SPs are expressible in a rather easy way in FHP-Murϕ. Thus,
we can use the algorithms developed for Markov Chains analysis to analyze finite character SPs too. This
is possible since each finite character SP X can be reduced to a Markov Chain X mc ; this is formalized in
the following.

Definition 2.18. Let X = (S, P, q) be a SP of finite character n w.r.t. a relation R, and let Q 0 , . . . , Qn−1
be an enumeration of the equivalence classes of R. Then, the Markov Chain X mc = (S 0 , P0 , q 0 ) is defined
as follows:

1. S 0 = S × {0, . . . , n − 1};

2. q 0 = (q, i0 ), where i0 is such that  ∈ Qi0 ;


2.3 Finite State/Discrete Time Stochastic Processes 17

3. let (s, i), (t, j) ∈ S 0 , then


(
0 P(s, π, t) if ∃π ∈ S ∗ : π ∈ Qi and πs ∈ Qj
P ((s, i), (t, j)) =
0 otherwise

where we denote with πs the path obtained by appending the state s at the end of the path π. Note
that P0 is well defined: in fact, even if there is more than one path with the required property, all these
paths, belonging to the same equivalence class (Qi in the formula above), define the same probability.

P see that0 X
To mc
is indeed a Markov Chain, P we have to show that,Pfor all (s, i) ∈ S 0 ,
0
(t,j)∈S 0 P ((s, i), (t, j)) = 1; this holds since (t,j)∈S 0 P ((s, i), (t, j)) = t∈S P(s, π, t) = 1, be-
ing π ∈ S ∗ such that π ∈ Qi and πs ∈ Qj .
Example 2.6. Let X = (S, P, s0 ) be the SP in Example 2.5. We have that X is an SP of finite character 3,
with Q0 = {}, Q1 = {π ∈ S ∗ | π 6=  and ∀0 ≤ k < |π| π(k) = s0 } and Q2 = (S ∗ \Q1 )\Q0 . So, we
have that X mc = ({(s0 , 0), (s0 , 1), (s0 , 2), (s1 , 0), (s1 , 1), (s1 , 2)}, P0 , (s0 , 0)), with

1 1
 
0 2 0 0 2 0
0 1 0 0 0 0
 
 
 
0 0 0 0 0 1
P0 = 
 


 0 0 0 0 0 1 

0 0 0 0 0 1
 
 
0 0 0 0 0 1

We now show that there is a 1-1 relation between paths in finite character SPs and corresponding Markov
Chains.
Definition 2.19. Let X = (S, P, q) be a SP of finite character n w.r.t. a relation R, and let Q 0 , . . . , Qn−1
be an enumeration of the equivalence classes of R. Let π ∈ Path(X , s) be a path in X . Then, the state
sequence π mc is built in a way such that, for all h ≥ 0, π mc (h) = (π(h), ih ), where ih is the index of the
equivalence class such that π|h ∈ Qih .
Proposition 2.6. Let X = (S, P, q) be a SP of finite character n w.r.t. a relation R, let Q 0 , . . . , Qn−1 be
an enumeration of the equivalence classes of R and let X mc = (S 0 , P0 , q 0 ). If π ∈ Path(X , s) is a path in
X , then π mc is a path on X mc (so, π mc ∈ Path(X mc , q 0 )).

Proof. Since π mc is indeed made of states in S 0 , so we only have to prove that, for all h ≥ 0,
P0 (π mc (h), π mc (h + 1)) > 0.

To do this, fix an h; we have that P0 (π mc (h), π mc (h+1)) = P0 ((π(h), ih ), (π(h+1), ih+1 )), where π|h ∈
Qih and π|(h+1) = (π|h)·π(h) ∈ Qih+1 . By Definition 2.18, we have that P0 ((π(h), ih ), (π(h+1), ih+1 ))
= P(π(h), π|h, π(h + 1)) > 0, where the last relation holds since π ∈ Path(X , s) by hypothesis.

Proposition 2.7. Let X = (S, P, q) be a SP of finite character n w.r.t. a relation R, let Q 0 , . . . , Qn−1 be
an enumeration of the equivalence classes of R and let X mc = (S 0 , P0 , q 0 ). If π ∈ Path(X mc , q 0 ) is a path
in X mc , then it exists exactly one path ρ ∈ Path(X , s) such that ρmc = π.

Proof. Suppose that π = (π(0), i0 )(π(1), i1 ) . . . (π(h), ih ) . . .. ρ is also the path π(0)π(1) . . . π(h) . . .. It
is obvious to see that ρ is the unique path with the desired property, since a different path σ should have at
least a different state, so σ mc 6= π.
18 Chapter 2. Theoretical Basis

Finally, we show that the probability measure Prob on a PRBTS X (Definition 2.16) is equivalent to the
one on the corresponding Markov Chain X mc (Definition 2.5). This motivates the dissertation of Chapter
5, in which we use our Markov Chain analysis algorithms to analyze also finite character SPs.
Proposition 2.8. Let X = (S, P, q) be a finite character SP, and let n, R, Q 0 , . . . , Qn−1 , i0 be, respectively,
the finite character of X , the relation w.r.t. which X is of finite character, the n equivalence classes of R
and the index of the equivalence class containing  (i.e. i0 is such that  ∈ Qi0 ). Then, for all finite paths ρ
of X ,

Prob{π ∈ Path(X , s) | ρ is a prefix of π} = Prob{π ∈ Path(X mc , (s, i0 )) | ρmc is a prefix of π}

Proof. By Definitions 2.16, 2.19 and 2.5 we have that Prob{π ∈ Path(X , s) | ρ is a prefix of π} =
k−1
Y k−1
Y
P(ρ) = P(ρ(h), ρ|h, ρ(h + 1)) = P0 ((ρ(h), ih ), (ρ(h + 1), ih+1 )) = P0 (ρmc ) = Prob{π ∈
h=0 h=0
Path(X mc , (s, i0 )) | ρmc is a prefix of π}.

2.4 T HE M URϕ V ERIFIER

In this section we give a short overview of the Murϕ verifier. For further details we refer the reader to
[22, 47].

We begin by giving the definition of the model underlying the Murϕ verifier.
Definition 2.20. A Nondeterministic Finite State System (shortened NFSS in the following) S is a 4-tuple
(S, I, A, next), where S is a finite set of states, I ⊆ S is the set of the initial states, A is a finite set of
labels and next : S → 2S×A is a function taking a state s as argument and returning a set next(s) of
pairs (t, a) ∈ S × A.

Note that the definition of NFSS is a simpler version of Definition 2.6 of PFSS, since it does not consider
probabilities. Thus, NFSSs function next returns, given a state s, the states in which s may go, together
with a label to manage the case of multiple transitions between the same states.

From a conceptual point of view Murϕ takes as input a NFSS S and checks whether a given invariant
property φ for S is satisfied.

An invariant property can be seen as a map from (the states of) S to the set B of boolean values; using
the speech of Section 2.2, it is an atomic proposition. Thus, the verification goal is to check if the given
invariant φ holds in each state s reachable from an initial state q ∈ I (i.e., φ(s) = 1). Of course, in general,
this check entails visiting all reachable system states.

Figure 2.4 shows the standard BF (Breadth-First) state space exploration algorithm. Essentially this is the
algorithm used by Murϕ to visit the state space of a given system S. The algorithm makes use of two main
memory data structures. A Queue, where states are stored and retrieved (in FIFO order) during the search,
and a Hash Table used to store all visited states.

Since S is a finite state system, the algorithm in Figure 2.4 always terminates since we never visit the same
state more than once.
2.4 The Murϕ Verifier 19

FIFO Queue Q;
HashTable T;

/* Returns true iff φ holds in all the reachable states */


b o o l BFS(NFSS S, AP φ)
{
let S = (S, I, A, next);

/* is there an initial state which is an error state? */


f o r e a c h s in I
{
i f (!φ(s))
/* error found, S does not satisfy φ */
return f a l s e ;
}

/* load Q with initial states */


f o r e a c h s in I Enqueue(Q, s);

/* mark the initial states as visited */


f o r e a c h s in I H a s h I n s e r t(T, s);

/* visit */
w h i l e (Q 6= ∅)
{
/* take from Q the state to be expanded */
s = Dequeue(Q);
/* s expansion */
f o r e a c h (s_next, a) in n e x t (s)
{
i f (!φ(s_next))
/* error found, S does not satisfy φ */
return f a l s e ;

i f (s_next i s n o t in T)
{
/* s next must be eventually expanded */
Enqueue(Q, s_next);
/* mark s next as visited */
H a s h I n s e r t(T, s_next);
} /* if */
} /* foreach */
} /* while */
/* here, Q is empty and T contains all the reachable states */
/* error not found, S satisfies φ */
return true;
} /* BFS() */

Figure 2.4: Explicit Breadth–First Search


20 Chapter 2. Theoretical Basis

2.4.1 M URϕ I NPUT L ANGUAGE

Murϕ input consists of a definition of the system S to be verified and a definition of the property φ to be
checked. Both definitions are stored in a file that we call here Murϕ description.

The Murϕ description language for system S is a high-level programming language for finite state asyn-
chronous concurrent systems (i.e. software like systems). Murϕ description language is high-level in the
sense that many features found in common high-level programming languages such as Pascal or C are part
of Murϕ. For example, Murϕ has user-defined data types, procedures, and parameterization of descriptions.

More in detail, a Murϕ description consists of:

• a set of declarations of constants, (finite) types, global variables;

• a collection of declarations of functions and procedures;

• a description of the initial states;

• a collection of transition rules;

• a set of invariants.

Note that the declaration of the global variables defines the Murϕ state. In fact, a Murϕ state is an assign-
ment of values to all of the global variables of the description. Hence, if the types of the global variables are
the finite sets D1 , . . . , Dn , and the NFSS to be described is S = (S, I, A, next), then S = D1 × . . . × Dn .

Thus, global variables defines the (static) structure of the model. On the other hand, the behavioral part (i.e.,
function next) of Murϕ is the collection of transition rules. Each transition rule is a guarded command
which consists of a condition (a boolean expression on the global variables) and an action (a statement that
can modify the values of the variables).

The condition and the action are both written in a Pascal-like language. The action can be an arbitrarily
complex statement containing loops and conditionals. No matter how complex it is, the action is executed
atomically, i.e. no other rule can change the variables or otherwise interfere with it while it is being exe-
cuted. More formally, if a rule r causes state s to become t, then next(s) ⊇ {(t, r)}.

Thus, an execution of the description is generated by executing the endless loop in Figure 2.5.

Note that nondeterministic aspect of Murϕ descriptions is exploited in the two random choices in Figure
2.5. The user has no control over how these choices are made, so a correct Murϕ program must do the right
thing no matter which rules are chosen. However, once a rule has been chosen, the action is deterministic
(there is a unique next state).

2.4.2 A TOY E XAMPLE

A small toy example should help to clarify the matter. Let us consider the finite state system S in Figure
2.6. The initial state of S is shown, as usual, with an ingoing arrow; moreover, nodes are labeled with state
values and edges are labeled with disturbance values.

Murϕ code for the NFSS S of Figure 2.6 is given in Figure 2.7 where we have examples of constants, types,
global variables, functions, initial states, transition rules and invariants.
2.4 The Murϕ Verifier 21

/* Make a random walk in the NFSS described by MD */


v o i d Make_a_run(MurϕDescription MD)
{
pick at random an initial state s among the ones in MD;
s_current = s;
w h i l e (1) { /* loop forever */
RulesEnabled = ∅;
f o r e a c h rule r in MD
{
i f (the r guard i s satisfied by s_current)
RulesEnabled = RulesEnabled ∪ r;
} /* foreach */
pick at random a rule r in RulesEnabled;
execute the r body on s_current, so obtaining s_next;
s_current = s_next;
} /* while */
} /* Make_a_run() */

Figure 2.5: Murϕ execution model

Figure 2.8 summarizes the output of the Murϕ verifier when given the input in Figure 2.7. Namely, the
Murϕ verifier returns an error trace, i.e. a (loopless) path in the graph in Figure 2.6 from an initial state to
a state violating the invariant property. If we replace the < sign in the invariant in Figure 2.7 with ≤ then
the invariant property is always satisfied since all reachable states of S have a value less than or equal to 5
(see Figure 2.6).
Remark 2.1. In the BF algorithm in Figure 2.4 only reachable states are visited and thus stored in the hash
table T. Hence the set of reachable states depends only on the system dynamics. For example, the set of
reachable states for the NFSS defined in Figure 2.6 is the integer interval [0, 5]. This set does not depend on
state type (the type of variable x in Figure 2.7) as long as state type contains the integer interval
[0, 5]. For example if in Figure 2.7 we change state type declaration to state type : 0..100
the set of reachable states is still the integer interval [0, 5].

0 0

2 2
1 3 2
1

0 1 1 1 5 0
0 1

2 1
2
2 4

0 2 0

Figure 2.6: A Nondeterministic Finite State System


22 Chapter 2. Theoretical Basis

/* constant declarations */
const
MAX_STATE_VALUE : 5;
MAX_DISTURB : 2;

/* type declarations */
type
/* integers from 0 to 10 */
state_type : 0 .. 10;
/* integers from 0 to 2 */
disturbance_type : 0 .. MAX_DISTURB;

/* (global) variable declarations */


var
/* x is a variable of type state_type */
x : state_type;

/* define next state function */


f u n c t i o n next(x: state_type; d : disturbance_type): state_type;
begin
i f (x <= MAX_STATE_VALUE - MAX_DISTURB) then
r e t u r n (x + d);
else
r e t u r n (x - d);
endif
end;

/* define initial state */


s t a r t s t a t e "startstate"
begin
x := 0;
end;

/* nondeterministic disturbances trigger system transitions */


r u l e s e t d : disturbance_type do
/* define parametric transition rule */
/* here, d varies in disturbance type, thus there are MAX DISTURB
+ 1 variants of rule "time step" */
r u l e "time step" t r u e ==>
begin
x := next(x, d);
end;
end;

/* define property to be verified */


i n v a r i a n t "x is not too big"
(x < MAX_STATE_VALUE);

Figure 2.7: Murϕ code for the NFSS in Figure 2.6


2.4 The Murϕ Verifier 23

Startstate startstate fired.


x:0
----------
Rule time step, d:1 fired.
x:1
----------
Rule time step, d:2 fired.
x:3
----------
Rule time step, d:2 fired.
The last state of the trace
(in full) is:
x:5
----------

Figure 2.8: Murϕ error trace for Murϕ model in Figure 2.7
C HAPTER 3

V ERIFICATION OF S AFETY P ROPERTIES

In this chapter, we will illustrate an algorithm for the verification of Discrete Time Markov Chains. This
algorithm is only able to verify finite horizon safety properties, e.g. it is able to state if the system under
analysis has an acceptable error probability. In Chapter 4 we will introduce a more general algorithm.

The chapter is organized as follows. In Section 3.1, we give the idea on which our algorithm lies. Then,
in Section 3.2 we will describe our algorithm in a more detailed way; here, we also give a proof of the
algorithm correctness and sketch its implementation within the Murϕ verifier. Section 3.3 close the chapter
by showing some meaningful experimental results and making a comparison with PRISM performances.

3.1 F INITE H ORIZON S AFETY V ERIFICATION OF M ARKOV C HAINS

We suppose we are given the following objects:

• a Markov Chain M = (S, P, q);


• an atomic proposition φ ∈ AP ;
• an integer k ∈ N;
• a probability threshold α ∈ [0, 1].

We want to design an algorithm able to check if p ≤ α, where p is the probability that a path of length k
starting from q reaches a state s satisfying φ (i.e. φ(s) = 1).

By using the notation of Section 2.2, this is equivalent to verify the following BPCTL formula:

q |= ¬[tt U≤k φ]>α . (3.1)

Note that, if φ models an error condition, this computation allows us to compute the probability that the
system modeled by M reaches an erroneous configuration in k execution steps. Thus, there are few prop-
erties that are verifiable via this algorithm, but they are important properties. In the rest of this chapter, we
will refer to states s such that φ(s) = 1 as error states.

We now give some definitions and propositions which will be useful to understand how the algorithm
works. Here, our main goal is to compute P [tt U≤k φ] (see Definition 2.11), since once this value has been
obtained, the truth value of (3.1) is straightforward.

We begin with a definition of a Markov Chain derived from M by means of φ.

25
26 Chapter 3. Verification of Safety Properties

Definition 3.1. Let M = (S, P, q) be a Markov Chain and let φ : S → B be an atomic proposition. We
define Markov Chain Mφ as follows: Mφ = (S, Pφ , q), where for all s, t ∈ S,


 P(s, t)
 if ¬φ(s)
Pφ (s, t) = 1 if φ(s) and (s = t)

0 if φ(s) and (s 6= t)

P
To see that Mφ is indeed a Markov Chain,P it is sufficient to show that, for all s ∈ S, t∈S Pφ (s, t) =
1; P if φ(s) = 1, then t∈S Pφ (s, t) = Pφ (s, s) = 1; otherwise, if φ(s) = 0, then
P this is true since,
P
t∈S φ (s, t) = t∈S P(s, t) = 1 (M is a Markov Chain by hypothesis).

In other words, Markov Chain Mφ is obtained from M by replacing all outgoing edges from any error
state s with just one edge leading back to s. Thus, once an error state is entered, M φ is forced to cycle on
it. This is equivalent to say that, if a path leads to an error state s, then it necessarily goes on repeating s;
more formally, if in a path π ∈ Path(Mφ ) there exists a n ∈ N such that φ(π(n)) = 1, then, for all h > n,
π(h) = π(n). This, in turn, entails that for Mφ the probability of reaching a state s satisfying φ in exactly
k steps equals the probability of reaching s in at most k steps.

Definition 3.1 is useful in Proposition 3.2, which our algorithm is based on. In order to prove Proposition
3.2, we have to enunciate Lemma 3.1.

Lemma 3.1. Let M = (S, P, q) be a Markov Chain, let k ∈ N be an integer, and let φ : S → B be an
atomic proposition. Then,

Prob{π ∈ Path(M) | ∃i ≤ k : φ(π(i))} = Prob{π ∈ Path(Mφ ) | φ(π(k))}

Proof. Note that, for all k ∈ N, we can limit to finite paths, so Prob{π ∈ Path(M) | ∃i ≤ k : φ(π(i))} =
Prob{π ∈ Pathk (M) | ∃i ≤ k : φ(π(i))} and Prob{π ∈ Path(Mφ ) | φ(π(k))} = Prob{π ∈
Pathk (Mφ ) | φ(π(k))}. Thus, it is sufficient to prove that

Prob{π ∈ Pathk (M) | ∃i ≤ k : φ(π(i))} = Prob{π ∈ Pathk (Mφ ) | φ(π(k))}

To this end, we will use the sequent objects:

• φk (Pathk (M)) = {π ∈ Pathk (M) | ∃i ≤ k : φ(π(i))}; this is the set Pathk without the paths on
which φ never holds;

• mk (φ, π) = min{0 ≤ i ≤ k | φ(π(i))}, where π ∈ φk (Pathk (M)); this is a function which, given
φ and a path π on which φ holds, returns the index of the first state of π on which φ holds;

• Pathφ (M) = {ρ = s0 . . . si | ∃π ∈ φk (Pathk (M)) : (ρ is a prefix of π ∧ i = mk (φ, π)))}; for


each path π in φk (Pathk (M)), this set contains the prefix of π that ends in the first state satisfying
φ.

To shorten formulas, we define A = φk (Pathk (M)), B = Pathφ (M), D = φk (Pathk (Mφ )), m =
mk (φ, π) and C = Pathk−mk (φ,π) ((S, P, π(mk (φ, π)))). Hence we have that

Prob{π ∈ Pathk (M) | ∃i ≤ k : φ(π(i))} =


3.1 Finite Horizon Safety Verification of Markov Chains 27

X k−1
Y
Prob{π ∈ Pathk (M) | π ∈ A} = P(π(j), π(j + 1)) = (3.2)
π∈A j=0

  
X m−1
Y X k−m−1
Y
 P(π(j), π(j + 1))  P(ρ(h), ρ(h + 1)) = (3.3)
π∈B j=0 ρ∈C h=0

  !
X m−1
Y X m−1
Y k−1
Y
P(π(j), π(j + 1)) =  P(π(j), π(j + 1)) 1 = (3.4)
π∈B j=0 π∈B j=0 h=m

X k−1
Y
Pφ (π(j), π(j + 1)) = Prob{π ∈ Pathk (Mφ ) | ∃i ≤ k : φ(π(i))} =
π∈D j=0

Prob{π ∈ Pathk (Mφ ) | φ(π(k))}

In the passage (3.2) we have used the Definition 2.5, while in transforming (3.3) to (3.4) we have used
the fact that, if M0 = (S 0 , P0 , q 0 ) is a Markov Chain, then for all s ∈ S 0 , n ∈ N and 1 ≤ k ≤ n,
P Qk−1 0 1
π∈Pathn (M0 ,s) j=0 P (π(j), π(j + 1)) = 1 . Moreover, the final equivalence is due to Definition 3.1:
in fact, a π ∈ Pathk (Mφ ), when reaches a state s in which φ holds, is forced to infinitely cycle on s with
probability 1 (every other transition from s has probability 0, so it is not activated).

Proposition 3.2. Let M = (S, P, q) be a Markov Chain, φ : S → B be an atomic proposition and q be


the distribution representing q. Then,

X
P [tt U≤k φ] = Prob{π ∈ Path(M) | ∃i ≤ k : φ(π(i))} = (qPφ k )s
s | φ(s)

Proof. By Lemma 3.1, it is sufficient to prove that

X
Prob{π ∈ Path(Mφ ) | φ(π(k))} = (qPφ k )s
s | φ(s)

From Markov Chain theory [3], we have that, for all s ∈ S,

Prob{π ∈ Path(Mφ ) | π(k) = s} = (qPφ k )s

So, we have that

Prob{π ∈ Path(Mφ ) | φ(π(k))} =


1 0
P This 0is an obvious property of Markov Chains, and it is a direct consequence of the Markov Chain property that, for all s ∈ S ,
t∈S P (s, t) = 1. Intuitively, both these two properties states that, if we are in a state s, then we surely go somewhere (including
cycling in s). The only difference is that the latter property only holds for 1 step, while the above one holds for any number of steps.
28 Chapter 3. Verification of Safety Properties

X
Prob{π ∈ Path(Mφ ) | ∨s | φ(s) π(k) = s} = Prob{π ∈ Path(Mφ ) | π(k) = s} =
s | φ(s)

X
(qPφ k )s
s | φ(s)

Example 3.1. Let S = {s0 , s1 } and M = (S, P, s0 ), with

!
4 1
P= 5 5
7 3
10 10

A graphical representation of M is shown in Figure 3.1.

Now, let φ be defined as follows: φ(s) = (s = s1 ), i.e. only state s1 satisfies φ. Then, we have that

! ! ! !
4 1 16 9 64 61
1 0
P0φ =I= P1φ = Pφ = 5 5 P2φ = 25 25 P3φ = 125 125
0 1 0 1 0 1 0 1
     
So, by Proposition 3.2, we have P [tt U≤0 φ] = 1 0 P0φ = 0; P [tt U≤1 φ] = 1 0 P1φ
   s
1   s1
1 ≤2 2 9 ≤3 3 61
= 5 ; P [tt U φ] = 1 0 Pφ = 25 ; P [tt U φ] = 1 0 Pφ = 125 .
s1 s1

1
5

4 3
5 s0 s1 10

7
10

Figure 3.1: Graphical representation of the Markov Chain M of Example 3.1

Proposition 3.2 suggests the idea that P [tt U≤k φ] may be computed by generating qPφ k ; after having
done this, it is not difficult to make the summation on the error states. So, we design an algorithm which,
by making a Breadth First (BF) visit of M, computes, for each BF level 0 ≤ i ≤ k − 1, the distribution
qPφ i as the content of the next level BF queue.

In Figure 3.2 we show function compute P, which realizes this computation. From a formal point of view,
compute P does not take as input a Markov Chain, since in Section 2.1.1 we have defined PFSSs as the
way which is used to define a Markov Chain. Thus, compute P input is a PFSS S, an integer k ∈ N
and an atomic proposition φ ∈ AP , while the output is P [tt U≤k φ] on the Markov Chain S mc . To this
end, w.r.t function BFS of Figure 2.4, we have that in compute P no check on already visited states is
done, and only non-error states are enqueued (as suggested by Proposition 3.2). Moreover, P [tt U ≤k φ] is
computed via variable prob err, which is incremented every time that an error state is encountered.
3.1 Finite Horizon Safety Verification of Markov Chains 29

Note that our algorithm is designed in order to avoid the generation of the transition matrix for Markov
Chain S mc , which is usually huge because of the known state explosion problem. The correctness of
function compute P of Figure 3.2 is proved in Proposition 3.4, by means of Lemma 3.3.

Lemma 3.3. Let S = (S, q, A, next) be a PFSS, k ∈ N be a nonnegative integer and φ : S → B be


an atomic proposition such that φ(q) = 0. Then, for all 0 ≤ i ≤ k holds the following. At the beginning
of level i in function compute P(S, k, φ), Q curr contains n pairs (s1 , p1 ), . . . , (sn , pn ), where n is the
number of the paths π1 , . . . , πn such that, for all 1 ≤ m ≤ n, πm satisfies the following properties:

1. |π| = i;

2. π(0) = q;

3. π(i) = sm ;

4. P(π) = p;

5. for all 0 ≤ j ≤ i, φ(π(j)) = 0.

Proof. The proof is done by induction on i.

As induction basis, we have that, at the beginning of BF level 0, Q curr contains only the pair (q, 1), and
in fact the only path of length 0 starting from q is q itself; finally, φ(q) = 0 and P(q) = 1 (i.e. the path
consisting only of q has probability 1).

As induction step, we suppose our statement valid for i, and we prove it for i + 1. To this end, let π 1 , . . . , πn
be the paths of length i + 1 starting from q such that, for all 1 ≤ m ≤ n and 0 ≤ j ≤ i + 1, φ(π m (j)) = 0.
Moreover, let Q curri be the queue Q curr at the beginning of level i. By induction hypothesis we have
that, for all 1 ≤ m ≤ n, the pairs (πm (i), P(πm |(i + 1))) (i.e., these pairs contains the last but one
state of the path, together with the probability of the path πm without the last state) were stored in the
queue Q curri . During the level i computation, these pairs are dequeued and expanded to create the queue
Q curri+1 (via the queue Q nexti+1 ).

Now, suppose by contradiction that exists a 1 ≤ m ≤ n such that (π m (i + 1), P(πm )) is not enqueued in
Q curri+1 during the level i computation. Since, by induction hypothesis, (π m (i), P(πm |(i + 1))) is on
Q curri , we have that (πm (i + 1), P(πm )) will be eventually enqueued in Q curri+1 . In fact, P(πm ) =
P(πm |(i+1))P(πm (i), πm (i+1)) and compute P makes exactly this computation, with p = P(πm |(i+
1)) (it is taken from Q curri ) and p next = P(πm (i), πm (i + 1)). Moreover, no error states are enqueued
at this level, so by induction hypothesis for all 1 ≤ m ≤ n and 0 ≤ j ≤ i + 1, φ(π m (j)) = 0.

On the other hand, suppose by contradiction that a pair (s, p) is enqueued in Q curri+1 , but for all 1 ≤
m ≤ n (s, p) 6= (sm , pm ) . By induction hypothesis, Q curri contains only pairs (πm (i), P(πm |(i + 1)))
satisfying conditions 1-5; thus, since only the states in Q curri are expanded, we have that there exists
1 ≤ m1 , . . . , mh ≤ n such that s = πml (i + 1) = sml for all 1 ≤ l ≤ h. Hence, p 6= pml = P(πml )
has to hold for all 1 ≤ l ≤ h. However, take a 1 ≤ l ≤ h; then, p is computed as p ∗ p next =
P(πml (i + 1))P(πml (i), πml (i + 1)) = P(πml ) = pml (where the last equivalence holds for the proof of
the first part of this lemma). This contradiction ends the proof.

Proposition 3.4. Let S = (S, q, A, next) be a PFSS, k ∈ N be a nonnegative integer and φ : S → B


be an atomic proposition. Then, function compute P(S, k, φ) of Figure 3.2 returns P [tt U ≤k φ] (on the
Markov Chain S mc ).
30 Chapter 3. Verification of Safety Properties

/* Compute P [tt U≤k φ] on the Markov Chain S mc */


compute_P(PFSS S, i n t k, AP φ)
{
Let S = (S, q, A, next);
/* if the initial state is itself an error state, the error
is reached with probability 1, since the initial state
is reachable with probability 1 */
i f (φ(q))
r e t u r n 1;
/* prob err accumulates P [tt U≤k φ] */
prob_err = 0;
/* Q curr and Q next are queues of state-probability pairs */
Q_curr = ∅;
Q_next = ∅;
/* initially, the only state to be expanded is the initial
one */
Enqueue(Q_curr, (q, 1));
/* BF levels from 0 to k - 1 */
f o r e a c h i s. t. 0 ≤ i ≤ k - 1
{
/* BF level i begins */
w h i l e (Q_curr 6= ∅)
{
/* dequeue from the current level... */
(s, p) = Dequeue(Q_curr);
/* s expansion */
f o r e a c h (s_next, a, p_next) in n e x t (s)
{
i f (φ(s_next))
/* s next is an error state */
prob_err = prob_err + p*p_next;
else
/* ...enqueue in the next level */
Enqueue(Q_next, (s_next, p*p_next));
} /* foreach */
} /* while */
/* BF level i finished, we now have to expand the states in
Q next */
Q_curr = Q_next;
Q_next = ∅;
} /* foreach */
/* exploration ended, now prob err contains the searched
value, i.e. P [tt U≤k φ] */
r e t u r n prob_err;
} /* compute_P() */

Figure 3.2: Computation of P [tt U≤k φ].


3.2 Explicit Finite Horizon Safety Verification of Markov Chains 31

mc
X Let M = S . By Proposition 3.2, we have to show only that the final value of prob err is
Proof.
k
(qPφ )s . To this end, let Π(s, k, r) = {π ∈ Pathk (Mφ , s) | π(k) = r} and Π(s, φ, k, r) = {π ∈
s | φ(s)
Path≤k (M, s) | ∃ρ ∈ Pathk (Mφ , s)∃0 ≤ i ≤ k : ρ(i) = r and ∀j < i ρ(j) 6= r and π = ρ|(i + 1) =
ρ0 . . . ρi } (i.e. Π(s, φ, k, r) contains all the paths of length at most k leading from s to r without going
through error states). Then, we have that

X
(qPφ k )s = (3.5)
s | φ(s)

X X X X
Pφ (π) = Pφ (π) = (3.6)
s | φ(s) π∈Π(q,k,s) s | φ(s) π∈Π(q,φ,k,s)

X X
P(π) (3.7)
s | φ(s) π∈Π(q,φ,k,s)

Here, the passage from (3.5) and (3.6) is a known property of Markov Chains [3]; the other passages are
due to the definition of Pφ (Definition 3.1).

To complete the proof, we only have to observe that, by Lemma 3.3 and its proof, (3.7) is exactly the
computation carried out for variable prob err in function compute P. In fact, the queue contains, for all
level i, the paths in {π ∈ Π(q, φ, i, s) | |π| = i and φ(s) = 0}, while the ones in {π ∈ Π(s, φ, i, r) | ∃1 ≤
j ≤ i : φ(π(j)) = 1} are used to increment prob err.

3.2 E XPLICIT F INITE H ORIZON S AFETY V ERIFICATION OF M ARKOV


C HAINS

In this section, taking as starting point the function compute P of Figure 3.2, we will present an efficient
disk based explicit algorithm which, given a PFSS S and a BPCTL formula F of the form (3.1), checks if
S |= F , i.e. if it holds that P [tt U≤k φ] ≤ α.

3.2.1 A LGORITHM D ESCRIPTION

Our algorithm makes use of the following data structures:

M is a cache table storing, for each slot, a pair (state, probability); the two fields of each slot are identified by
the names state and prob, respectively. It has a fixed size, set at the beginning of the verification
as a parameter.

Q curr, Q next are, respectively, the queues for the current and the next level front of the BF visit.
Since M does not grow, these are the only data structures which may fill the memory. However,
our implementation is designed in order to efficiently use the disk when the available RAM is not
sufficient to store both M and the queues (see Section 3.2.3).
32 Chapter 3. Verification of Safety Properties

From the behavioral point of view, instead, our algorithm is composed of three main functions:

Safety (Figure 3.3) efficiently implements the computation described in Figure 3.2, by making use of
the other (auxiliary) functions.
Insert (Figure 3.4) uses a cache table M in RAM to reduce the queue length, thus saving computation
time. In fact, every time it is necessary to enqueue a new pair (state s, probability p), Insert(s,
p) is called. If a pair (s, p0 ) is already stored in cache M, we simply update the stored probability
p0 in M, adding p to it; otherwise, we store (s, p) in M. If this causes a collision (i.e., if the slot of M
in which we have to put (s, p) is already occupied), we call function Checktable to empty M and
thus free the needed cache slot.
Checktable (Figure 3.4) simply flushes M into Q next. It is actually the only function that enqueues
values in Q new and is used by function Insert to free M when a collision occurs. Checktable
is also called at the end of the while in function Safety (Figure 3.3), so that all states reached in
the current level will be expanded in the next one.

Remark 3.1. The algorithm described in Figures 3.3 and 3.4 extends the compute P function of Figure
3.2 by means of a cache table M. Indeed, M is used as a temporary buffer before inserting a state and its
probability in the queue, thus avoiding the insertion of some duplicates. Moreover, if no collision occurs, all
duplicates are avoided. In fact, if we were not using M, for each state s at level i we would have n copies of
s in the queue, where n is the number of paths of length i leading to state s from initial state q without going
through an error state (this is exactly what happens in Figure 3.2, see Lemma 3.3). On the other hand, by
using M and assuming that there are no collisions, these n copies collapses in one cache slot. Thus, our
queue will contain, after the call to Checktable at the end of the level, one copy of s. On the other hand,
if some collisions occur in M, we will have h copies of s in the queue, with h << n. More formal details on
what happens to the cache and the queue may be found in Section 3.2.2.

Obviously, this mechanism saves queue space as well as computation time. In fact, if the RAM available for
M grows, there will be less collisions, hence less duplicated states in the queue, so the number of states to
be explored decreases and, finally, the computation time is reduced. In this way we are creating a trade-off
between space and time; for this reason, M should be as large as possible.

3.2.2 A LGORITHM C ORRECTNESS

To prove the correctness of the algorithm shown in Figures 3.3 and 3.4, we only have to show that the
caching mechanism does not affect the validity of Proposition 3.4. To show this, consider again the algo-
rithm without the cache (Figure 3.2); as we showed in Lemma 3.3, if there are n paths π 1 , . . . , πn leading
from q to s in i steps without going through an error state, then at the end of level i there will be n copies of
s in the queue. Suppose now that m of these copies, namely (s, p i1 ), . . . , (s, pim ) (with m ≤ n), collapse in
only one queuePm entry, together with the sum of the corresponding probabilities, i.e. the queue now contains
(s, p), being j=1 pij , instead of the m entries (s, pi1 ), . . . , (s, pim ). It is easy to see that the computation
of prob err still remains correct. In fact, if φ(s) = 0 prob errPwill not be updated, otherwise it will be
incremented directly by p in the algorithm with the cache, and by m j=1 pij (with m different incrementing
statements) with the other one.

Thus, to prove the correctness of function Safety, it is sufficient to show that the caching mechanism
just collapses states in the queue as described above. To this end, suppose that a state s is reached n times
in BF level j, from the queue pairs (s1 , p1 ), . . . , (sn , pn ) (note that could happen that si = sj for some
1 ≤ i, j ≤P n). If no cache collision occurs at level j, then there will be a cache slot h such that M[h] = (s, p),
n
with p = i=1 pi P(si , s). Thus, instead of n pairs (s, p1 ), . .P . , (s, pn ), being pi = pi P(si , s), only one
n
pair (s, p) will be enqueued (when level j finishes). Since p = i=1 pi P(si , s), the computation is correct
by the above reasoning.
3.2 Explicit Finite Horizon Safety Verification of Markov Chains 33

/* Main function, returns true iff S |= F */


b o o l Safety(PFSS S, formula F )
{
Let S = (S, q, A, next);
Let F ≡ ¬[tt U≤k φ]>α ;
/* this function works only if F is of the above type */
i f (φ(q))
return true;
prob_err = 0;
Enqueue(Q_curr, (q, 1));

f o r e a c h i s. t. 0 ≤ i ≤ k - 1
{
/* M is a cache table for the current BF level, so it has
to be cleared at the beginning of the level itself */
clear all slots in M;

w h i l e (Q_curr 6= ∅)
{
(s, p) = Dequeue(Q_curr);
/* s expansion */
f o r e a c h (s_next, a, p_next) in n e x t (s)
{
i f (φ(s_next))
{
prob_err = prob_err + p*p_next;
i f (prob_err > α)
/* property does not hold */
return f a l s e ;
} /* if */
else
/* Instead of enqueuing in Qn ext, M is used */
Insert(s_next, p*p_next);
} /* foreach */
} /* while */

/* here, Q curr is empty but Q next does not contain all the
states to be expanded */
Checktable();
/* now Q_next contains all the states to be expanded */
Q_curr = Q_next;
Q_next = ∅;
} /* foreach */

/* property holds */
return true;
} /* Safety() */

Figure 3.3: Function Safety


34 Chapter 3. Verification of Safety Properties

/* Manage the accesses to M */


Insert(state s, double p)
{
h = hash(s);

i f (M[h] i s free)
{
/* insert the pair (s, p) in M */
M[h].state = s;
M[h].prob = p;
} /* if */
e l s e i f (M[h].state == s)
/* the probability to reach s is the preceding one plus the
new probability p, thus update M[h].prob */
M[h].prob = M[h].prob + p;
else
{
/* s could not be inserted, since its slot is occupied by
another state */
Checktable();
/* there is space to insert now */

/* now, the pair (s, p) can be inserted in M */


M[h].state = s;
M[h].prob = p;
} /* else */
} /* Insert() */

/* Empty M by filling Q next */


Checktable()
{
move M in Q_next and clear M;
} /* Checktable() */

Figure 3.4: Functions Insert and Checktable


3.3 Experimental Results 35

In the most general case, suppose that m collisions c1 , . . . , cm occurs between the n visits of s, and let
l1 , . . . , lm be the indexes of the states causing a collision. Then, at the end of level j the queue Q next will
P li
contain m + 1 copies of s, with probabilities i=1 pi P(si , s). We have so proved the following theorem.
Theorem 3.5. Let F ∈ LBP CT L be a BPCTL formula such that F ≡ ¬[tt U≤k φ]>α , and let S =
(S, q, A, next) be a PFSS. Then, Safety(S, F ) returns true iff S |= F .

3.2.3 A LGORITHM I MPLEMENTATION

We implemented the algorithm shown in Section 3.2.1 within an already existent model checker: this allows
us not to deal with the definition from scratch of an input language and with the creation of the basic
verification structures. To this end, we choose the Murϕ model checker [47] since its input language is
easily modifiable in order to specify Markov Chains; we call the resulting tool FHP-Murϕ (Finite Horizon
Probabilistic Murϕ).

Thus, we modified the Murϕ original input language (see Section 2.4.1) in order to make it able to define
a PFSS S. To this end, we replace boolean conditions on rule guards with probabilities, and use BPCTL
formulas instead of invariants; moreover, the BPCTL formulas accepted by FHP-Murϕ are slightly more
general than those of Definition 2.8, since they accept all the possible orderings: >, ≥, <, ≤. The technical
details for the FHP-Murϕ input language may be found in Appendix A, together with the Lex, Yacc and
C++ code for the FHP-Murϕ compiler.

For what concerns the implementation of the verification algorithm, it is built on the pseudocode of Figures
3.3 and 3.4, by properly modifying the existent Murϕ code. In fact, having now probability instead of
booleans on rule guards, we are indeed able to define a function implementing the next structure used in
Figures 3.2 and 3.3, as well as the Murϕ definition of the starting state allows to know which is q.

Finally, we have pointed out in Section 3.2.1 that the queues are only partially stored on RAM, being the
remaining part maintained on disk storage. This is efficiently done by means of the mechanism described
in [21]; for exposition completeness, we show in Figure 3.5 the pseudocode of enqueue and dequeue oper-
ations, and of the related functions.

The complete FHP-Murϕ code for the verification may be found in Appendix B.

3.3 E XPERIMENTAL R ESULTS

To show effectiveness of our approach we run two kind of experiments.

First, in Section 3.3.1, we compare verifications of finite horizon safety formulas done by FHP-Murϕ with
verifications of the same models and formulas done by the probabilistic model checker PRISM [52].

Second, in Section 3.3.2, we run FHP-Murϕ to verify a finite horizon safety property on a quite large
probabilistic hybrid systems.

3.3.1 P ROBABILISTIC D INING P HILOSOPHERS

In this section we give our experimental results on using FHP-Murϕ on the probabilistic protocols included
in PRISM distribution [52]. We do not consider the protocols that lead to Markov Decision Processes or to
36 Chapter 3. Verification of Safety Properties

v o i d Enqueue(state s, double p)
{
i f (tail_queue i s full) swap_out();
/* now, tail queue is not full */
insert (s, p) into tail_queue;
tail_queue_elements++;
} /* Enqueue() */

/* hyp: Dequeue is always called on a nonempty queue */


(state, double ) Dequeue()
{
i f (head_queue == ∅) swap_in();
/* head queue is not empty now */
head_queue_elements--;
r e t u r n top of head_queue;
} /* Dequeue() */

/* Moves part of the queue from RAM to disk */


v o i d swap_out()
{
append tail_queue to swapout_file;
tail_queue_elements = 0;
} /* swap_out() */

/* Moves part of the queue from disk to RAM */


v o i d swap_in()
{
i f (swapin_file 6= ∅)
load head_queue with at most ram_queue_size states from
swapin_file;
else
{
/* swapin file is empty, we use swapout file */
swap the swapin_file and swapout_file;
i f (swapin_file 6= ∅)
load head_queue with at most ram_queue_size states from
swapin_file;
else
{
/* also swapout file is empty, we use tail queue */
swap the head_queue and tail_queue;
i f (head_queue == ∅)
/* underflow error */
error("queue is empty");
} /* else */
} /* else */
head_queue_elements = number of states in head_queue;
} /* swap_in() */

Figure 3.5: Enqueue and Dequeue operations in the queue disk storage mechanism
3.3 Experimental Results 37

Continuous Time Markov Chains, since FHP-Murϕ cannot directly deal with them. Hence we only consider
Pnueli-Zuck [51] and Lehmann-Rabin [45, 46] probabilistic dining philosophers protocols. Moreover, we
modify PRISM definitions for such protocols in order to be able to define a significant finite horizon safety
property on them. In fact, the PCTL formulas for these protocols included in the PRISM distribution are
not finite horizon safety properties.

The probabilistic dining philosophers protocol can be described as follows. The situation is the same as the
standard dining philosophers problem: there are n philosophers sitting at a round table with n plates and n
forks. Each philosopher behaves as follows. Initially, he thinks for an unpredictable time, then he becomes
hungry and repeatedly tries to get both forks at his left and his right side; he succeeds in this try if and only
if no one of his two neighbors has already taken the adjacent fork. When a philosopher has gotten both the
forks, he eats for some time, then he releases both forks and returns in the thinking state.

The probabilistic extension proposed by Pnueli-Zuck consists in having a random choice in two points:

• a philosopher who is thinking may continue to think with a 20% probability, and may try to get the
forks with a 80% probability;

• a philosopher who is trying to get his forks, will try the left one first with probability 50%, and the
right one first with the same probability.

On the other hand, Lehmann-Rabin proposed a fairness extension to the Pnueli-Zuck protocol. In fact,
at any time tick, only one philosopher may perform an action. Thus, in the Lehmann-Rabin protocol, a
philosopher may make any action if and only if it will not cause another philosopher to have waited more
than a given threshold without having done any action.

Our modifications to PRISM protocols consist in adding variables to count the number of times that a
philosopher fails in getting both forks. E.g., in the Pnueli-Zuck protocol, we replaced the code fragment
in Figure 3.6 with the one in Figure 3.7. This modification obviously does not change the protocols be-
havior, but allows us to write a BPCTL formula which requires that the probability of these counters to be
too high, i.e. equal to a given maximum value (MAX CONT in the following), is low. More formally, if
P (MAX CONT, k) is the probability that a philosopher counter reaches MAX CONT in at most k steps, then
P (MAX CONT, k) has not to overtake a given probability threshold α. This corresponds to verify quality of
service properties, which are very frequent in practice. Figure 3.8 shows the PRISM code for the BPCTL
property to be verified, stating that the probability that a counter reaches MAX CONT has to be at most 10 −3 .

On the other hand, we translate into FHP-Murϕ the modified PRISM definitions, so that for each protocol,
FHP-Murϕ and PRISM definitions specify exactly the same Markov Chain; e.g., in Figure 3.9 we have the
FHP-Murϕ code corresponding to the PRISM code fragment of Figure 3.7.

To assess FHP-Murϕ effectiveness, in Tables 3.1 and 3.2 we compare the results obtained with FHP-
Murϕ and with PRISM on, respectively, Pnueli-Zuck and Lehmann-Rabin protocols (modified as described
above). In these experiments, we always set k = 20 (see Figures 3.8 and 3.9), and we use a machine with
2 processors (both INTEL Pentium III 500MHz) and 2GB of RAM. As in Figure 3.9, NPHIL indicates the
number of philosophers, while MAX CONT, as already said, is maximum number of times that a philosopher
can fail in getting both forks before dying. For what concerns the verification options, the FHP-Murϕ RAM
memory occupations are a priori fixed by the option -mM . On the other hand, PRISM options are the
default ones in the first rows of Tables 3.1 and 3.2, while the “N/A” value means that PRISM was unable
to complete the verification. In this latter case, also the -m and -s options (totally MTBDD and algebraic
verification algorithm respectively) have been used, with the same failing result.

This is due to the fact that these protocols requires some non-trivial mathematical operations, that is exactly
the case in which OBDDs, and thus MTBDDs, reach their worst memory occupation. On the contrary,
38 Chapter 3. Verification of Safety Properties

module phil1
p1: [0..10] i n i t 0;
. . . . .
// if the philosopher has failed to get both the forks,
// he has to try to get them from the beginning
[] p1=6 -> (p1’=1);
// the same as above, but the philosopher tried the two
// forks in the inverse order
[] p1=7 -> (p1’=1);
. . . . .
// the philosopher has eaten and released the forks, he
// can return to thinking
[] p1=10 -> (p1’=0)
endmodule

Figure 3.6: Pnueli-Zuck algorithm fragment to be modified in PRISM

module phil1
p1: [0..10] i n i t 0;
cont1: [0..3] i n i t 0;
. . . . .
// w.r.t. Figure 3.6, we also increment cont1 till the
// saturation value MAX_CONT
[] p1=6 & cont1!=MAX_CONT -> (p1’=1) & (cont1’=cont1+1);
[] p1=6 & cont1=MAX_CONT -> (p1’=1);
[] p1=7 & cont1!=MAX_CONT -> (p1’=1) & (cont1’=cont1+1);
[] p1=7 & cont1=MAX_CONT -> (p1’=1);
. . . . .
// when returning at the thinking state, we have to reset
// cont1
[] p1=10 -> (p1’=0) & (cont1’=0);
endmodule

Figure 3.7: Pnueli-Zuck algorithm modified fragment in PRISM

P<=0.001 [ t r u e U<=20 ((cont1=MAX_CONT) | (cont2=MAX_CONT)


| (cont3=MAX_CONT))]

Figure 3.8: BPCTL formula in PRISM


3.3 Experimental Results 39

/* returns the probability that p[i] becomes next (returns


0 when a transition is not allowed); NPHIL is the number
of philosophers */
f u n c t i o n calc_prob(i : 1..NPHIL; next : 0..10) : prob;
begin
switch p[i]
/* p[i] corresponds to PRISM pi */
.
.
.
/* this computes the probability of the first two rules of
Figure 3.7 */
c a s e 6:
i f (next = 1) then r e t u r n 1.0 / NPHIL;
e l s e r e t u r n 0.0;
endif;
/* this computes the probability of the second two rules
of Figure 3.7 */
c a s e 7:
i f (next = 1) then r e t u r n 1.0 / NPHIL;
e l s e r e t u r n 0.0;
endif;
.
.
.
e n d s w i t c h ; end;

/* set of rules describing the system behavior */


r u l e s e t philosophers : 1..NPHIL do
r u l e s e t next : 0..10 do
r u l e "next"
calc_prob(philosophers, next) ==>
begin
/* philosopher i changes state to next */
p[i] := next;
/* cont[i] corresponds to PRISM conti */
i f (next = 1 & (p[i] = 6 | p[i] = 7) &
cont[i] != MAX_CONT)
/* cont[i] incremented when there is a transition from
state 6 or 7 to state 1, and cont has not reached its
saturation value */
then cont[i] := cont[i] + 1;
endif;
/* counter reset */
i f (p[i] = 10 & next = 0) then cont[i] := 0;
endif;
end; end; end;

pctl [ t r u e u n t i l <= 20
( e x i s t s i : 1..NPHIL do (cont[i] = MAX_CONT) e n d e x i s t s )
] <= 0.001;

Figure 3.9: Pnueli-Zuck algorithm in FHP-Murϕ


40 Chapter 3. Verification of Safety Properties

mathematical operations in the computation of the transition function next is hardly a problem for FHP-
Murϕ, that succeeds in completing these verifications.

NPHIL MAX WAIT Result Murϕ memory PRISM memory Murϕ time PRISM time
3 3 false 2.000000e+02 9.057000e-01 5.197000e+01 1.487000e+00
3 4 false 2.000000e+02 1.684400e+00 5.261000e+01 2.507000e+00
4 3 false 2.000000e+02 2.810660e+01 2.429400e+02 2.87200e+01
4 4 false 2.000000e+02 6.626590e+01 2.441700e+02 7.111200e+01
5 3 false 2.000000e+02 9.168246e+02 1.408290e+03 1.023468e+03
5 4 false 2.000000e+02 N/A 1.412210e+03 N/A
8 3 false 1.000000e+03 N/A 2.137907e+05 N/A

Table 3.1: Experimental results for the verification of a finite horizon safety property on the Pnueli-Zuck
protocol. The experiments were carried out on a machine with 2 processors, both Pentium III 500MHz,
with 2GB of RAM. Time is in seconds, memory occupations are in MB.

NPHIL MAX WAIT Result Murϕ memory PRISM memory Murϕ time PRISM time
3 3 false 8.000000e+02 3.906250e+01 1.040330e+03 8.455600e+01
3 4 true 8.000000e+02 7.014830e+01 1.041700e+03 1.211470e+02
4 3 false 8.000000e+02 N/A 3.430774e+04 N/A

Table 3.2: Experimental results for the verification of a finite horizon safety property on the Lehmann-Rabin
protocol. The experiments were carried out on the same machine of Table 3.1. Time is in seconds, memory
occupations are in MB.

3.3.2 A “R EAL WORLD ” P ROBABILISTIC H YBRID S YSTEM

In this section we show our experimental results on using FHP-Murϕ for the analysis of a real world hybrid
system. Namely, the Control System for the Gas Turbine of a 2MW Electric Co-generative Power Plant
(ICARO) in operation at the ENEA Research Center of Casaccia (Italy).

Our control system (Turbogas Control System, TCS, in the following) is the heart of ICARO and is in-
deed the most critical subsystem in ICARO. Unfortunately TCS is also the largest ICARO subsystem, thus
making the use of model checking for such hybrid system a challenge.

TCS task is to maintain the ICARO parameters within the required safety ranges, when the user demand
varies. However, in [17] we showed that, if the speed of variation of the user demand for electric power
(MAX D U in the following) is greater than or equal to 25 kW/sec, TCS fails in its task. This result has been
obtained by modeling TCS in a modified version of Murϕ, in which it is allowed to use finite precision real
numbers in the modeling.

We now want to know what is the probability that a finite execution of the TCS reaches an error state, when
the user demand variation is modeled by a Markov Chain. Here, we say that a TCS state is an error state iff
one of ICARO parameters is outside its given safety range.

To this end, let p(u, i) be a function defined as follows:


3.3 Experimental Results 41

(u−u0 )|u−u0 |

 0.4 + β
 M2 if i = 1
p(u, i) = 0.2 if i = 0 (3.8)
0.4 + β (u0 −u)|u−u 0|

if i = −1

M2
M
where M =MAX U is the maximum user demand value, u0 = 2 is the user demand setpoint, and β is a
verification parameter.

Denoting with u(t) the user demand value at time t, we can define the (stochastic) dynamics for the user
demand as follows:


 min(u(t) + α, M )
 with probability p(u(t), 1)
u(t + 1) = u(t) with probability p(u(t), 0) (3.9)

max(u(t) − α, 0) with probability p(u(t), −1)

where α =MAX D U.

In this way, we have that the further u(t) from u0 , the higher the probability to return towards u0 , i.e. to
decrement u(t) if u(t) > u0 and to increment it otherwise.

To see that (3.9) is indeed a Markov Chain, we first have to point out that, since we discretize the time
t, we have that (3.9) has indeed a finite number of states. Then, it is sufficient to observe that, for all β,
the sum of the outgoing transitions is obviously 1. Moreover, since 0 ≤ (u(t)−u0M)|u(t)−u
2
0|
≤ 1, as long
as −0.4 ≤ β ≤ 0.4 holds, all probability values are between 0 and 1. In our experiments, we always set
β = 0.4.

With FHP-Murϕ the definition of Markov Chain (3.9), starting from the TCS model, is quite simple. This is
done in Figure 3.10, where user demand(u, d u) computes p(u, d u) (3.8) and function make1step
updates the system state, in particular updates u as described in (3.9).

r u l e s e t d_u : -1 .. 1 do
/* disturbance d u takes values -1, 0 and 1 */
r u l e "time step" user_demand(u, d_u) ==>
begin
/* computes the TCS answer to the user demand u in a time
unit */
make1step(u, d_u);
end;
end; /* user demand disturbance */

Figure 3.10: Ruleset for TCS with probabilistic user demand

We are interested in cases where the error probability is greater than 0. From the results in [17] we know
that this is the case if we choose MAX D U greater than or equal to 25 and the horizon value no smaller than
the transition graph diameter.

In our experiments here we choose our horizon as follows. Let Diam(n) be the minimuml numbermof states
needed to reach an error state when MAX D U = n. We set our horizon k to be equal to Diam(n) 100. In
100
this way we check the error probability in the error neighborhood.

Our results are in Table 3.3. The first columns meaning is straightforward, while in the field “Probability”
is shown the error probability, i.e. the value P [tt U≤k φ], being φ the atomic proposition denoting the error
42 Chapter 3. Verification of Safety Properties

MAX D U Visited States Rules Fired k CPU Time Probability


35 2.226036e+06 6.602763e+06 1400 5.026302e+04 1.076644427e-04
45 1.834684e+06 5.439327e+06 1300 4.140315e+04 9.957147381e-05
50 8.318900e+04 2.462850e+05 900 2.212360e+03 3.984375e-03

Table 3.3: Experimental results for the verification of a finite horizon safety property on the Turbogas
Control System. The experiments were carried out on a machine with 2 processors, both Pentium III
500MHz, with 2GB of RAM. Time is in seconds.

states. As in Section 3.3.1, we used a machine with 2 processors (both INTEL Pentium III 500MHz) and
2GB of RAM, and give to FHP-Murϕ the option-m500 to force it to use exactly 500 MB of RAM.

Table 3.3 allows us to evaluate the probability of an error state when MAX D U is greater than or equal to 25.
Note that such a probability is rather small, suggesting that, in many cases, even if MAX D U is greater than
25, TCS behavior may be acceptable. This kind of evaluations are not possible with the nondeterministic
verification of TCS carried out in [17].
C HAPTER 4

V ERIFICATION OF BPCTL P ROPERTIES

In this chapter, we move from the algorithm for finite horizon safety properties presented in Chapter 3 to an
algorithm for all finite horizon properties, i.e. an algorithm able to verify all BPCTL formulas. In particular,
by allowing the specification of formulas with nested untils, this algorithm is able to check robustness and
reliability properties, as we will show in Section 4.2.

The chapter is organized as follows. In Section 4.1 we describe our algorithm. Namely, in Section 4.1.1 we
give a formal description of our algorithm; Section 4.1.2 provides three simple running examples; Section
4.1.3 formally proves the algorithm correctness; finally, in Section 4.1.4 we sketch our implementation of
the algorithm in Section 4.1.1 within the FHP-Murϕ verifier. Section 4.2 close the Chapter by showing
some meaningful experimental results and making a performance comparison with PRISM.

4.1 E XPLICIT BPCTL M ODEL C HECKING

In this section we present an explicit algorithm to verify if a PFSS S = (S, q, A, next) satisfies a given
BPCTL formula F ; thus, we want to determine if S |= F holds.

By Definition 2.9, it is clear that the most difficult case in the verification of a BPCTL formula is to compute
the truth value of U-subformulas. In Chapter 3, we solved a simplified version of this problem (consisting
in forcing the subformulas of the given U-formula to be atomic propositions) by implementing a Breadth
First (BF) visit of S state space.

However, suppose we have to verify a formula with nested U-formulas, which are exactly the kind of
formulas defining the robustness and reliability properties we are interested in here. Thus, let F ≡
[tt U≤k1 Φ1 ]wα , with Φ1 ≡ [tt U≤k2 φ]wβ , being φ an atomic proposition. To determine if S |= F
(i.e. q |= F ) we have to check if t |= Φ1 , for all states t that are reachable from q in at most k1 steps.
This entails that we have to start, for all t, a nested BF visit. Fortunately, it is possible to avoid some of
these nested visits, namely those starting from a state t such that t |= Φ 1 has already been evaluated by a
preceding nested call. In fact, we may store in a cache t, Φ1 and the truth value of t |= Φ1 , and then reuse
this information when t is reached again.

This suggests to extend this approach also to intermediate results. As an example, consider again the
problem of evaluating if t |= Φ1 , and suppose that there exists a state r which is reachable from t with n
different paths π1 , . . . , πn , all of length at most k2 . Let 1 ≤ i ≤ n; if we store in RAM a cache maintaining
r, Φ1 , |πi | and pi = Pr [tt U≤k2 −|πi | φ], then p1 , . . . , pi may be used to avoid the nested call when r is
reached via πi+1 . Note that pi is the probability of a path formula, instead of a truth value, because this
allows us to make further optimizations.

However, this is not possible by using the BF visit of Chapter 3, which only allows us to compute, for all t,
Pt [tt U≤k2 φ]. Thus, the algorithm we propose is based on a Depth First (DF) visit of S. Moreover, it uses

43
44 Chapter 4. Verification of BPCTL Properties

a cache to store intermediate results, in order to speed up the verification task. Finally, our DF algorithm
maintains some black-box properties of the BF algorithm in Chapter 3:

• it is disk based, i.e. stores part of the DF stack on disk. Since the DF stack is the only structure which
may grow in our algorithm, this allows us to face the well known state explosion problem, because
of the large size of modern hard disks;
• it trades memory with time, i.e. the more RAM available, the faster our computations. In fact, having
more RAM available allows us to allocate a larger cache, so avoiding a higher number of nested
visits.

4.1.1 A LGORITHM D ESCRIPTION

In this section we give a formal description of the algorithm verifying a generic BPCTL formula. Let
S = (S, q, A, next) be a PFSS and F be a BPCTL formula; we want to check if S |= F holds. As already
said in Section 2.2.1 (see Theorem 2.4), this algorithm will consider only paths of finite length.

The main function BPCTL, taking S and F and returning true iff S |= F , is shown in Figure 4.1. This
function uses, as auxiliary functions, the following ones:

BPCTL rec (Figure 4.1), is a recursive function that browses the syntactical structure of F , calling itself
or one of the other auxiliary functions.
evalX (Figure 4.1), evaluates X-formulas, i.e. checks if s |= [X Φ]wα , being s ∈ S. This function simply
visits all s successors t1 , . . . tn , properly incrementing the 0-initialized variable sum for each i such
that ti 6|= Φ. In this way, if variable sum reaches a value p such that p w α, then true is returned,
since Ps [X Φ] ≥ p w α. Otherwise, false is returned, since Ps [X Φ] < p @ α (supposing that @
is the symbol < when w is ≥, and ≤ when w is >). Note that, in this latter case, sum = P s [X Φ].
evalU (Figure 4.2), is dedicated to the evaluation of U-formulas, i.e. checks if s |= [Φ U ≤k Ψ]wα , being
s ∈ S. To this end, it uses a cache C and the function DF Search.
DF Search (Figure 4.2), is an auxiliary function for evalU, performing a finite horizon DF visit of the
PFSS S.

As already said, the most difficult case is the one concerning the evaluation of U-formulas. This com-
putation is carried out by the two functions evalU and DF Search. DF Search computes the value
Ps [Φ U≤k Ψ] by making a DF Search of the state space, starting from the state s; evalU simply calls
DF Search. Both functions make use of a cache C, which is organized as follows. Each cache slot of
index i contains:

• a state s (C[i].state in Figure 4.3);


• a U-formula F ≡ [Φ U≤k Ψ]wα (C[i].form);
• an integer h (C[i].horizon);
• Ps [Φ U≤h Ψ] (C[i].prob).

In this way, we exploit the recursiveness of the DF visit, which allows us to compute, for every state t
reached during the computation of s |= F , the value Pt [Φ U≤h Ψ], where h is the number of steps that t
4.1 Explicit BPCTL Model Checking 45

/* Main function */
b o o l BPCTL(PFSS S, formula F )
{
Let S = (S, q, A, next);

/* make in the proper way the first call to BPCTL rec */


r e t u r n BPCTL_rec(S, q, F );
} /* BPCTL() */

/* BPCTL recursive step */


b o o l BPCTL_rec(PFSS S, state s, formula F )
{
i f (F ≡ 1)
return true;
e l s e i f (F i s an atomic proposition p)
r e t u r n p(s) = 1;
e l s e i f (F ≡ [X Φ]wα )
r e t u r n evalX(S, s, F );
e l s e i f (F ≡ [Φ U≤k Ψ]wα )
r e t u r n evalU(S, s, F );
/* recursive cases */
e l s e i f (F ≡ ¬F1 )
r e t u r n !BPCTL_rec(S, s, F1 );
e l s e i f (F ≡ F1 ∧ F2 )
r e t u r n BPCTL_rec(S, s, F1 ) && BPCTL_rec(S, s, F2 );
} /* BPCTL_rec() */

/* Evaluate X-formulas */
b o o l evalX(PFSS S, state s, formula F )
{
Let S = (S, q, A, next);
Let F ≡ [X Φ]wα ;

sum = 0;
/* variable sum accumulates Ps [F ] */
f o r e a c h (s_next, a, p_next) in n e x t (s)
{
i f (BPCTL_rec(S, s_next, Φ))
{
sum = sum + p_next;
i f (sum w α)
return true;
} /* if */
} /* foreach */
return f a l s e ;
} /* evalX() */

Figure 4.1: Functions BPCTL, BPCTL rec and evalX


46 Chapter 4. Verification of BPCTL Properties

Cache C;

/* Evaluate U-formulas */
b o o l evalU(PFSS S, state s, formula F )
{
Let F ≡ [Φ U≤k Ψ]wα ;
(valid, result) = try_to_evaluate(C, s, F );
i f (valid)
/* it was possible to evaluate if s |= F by using only C */
r e t u r n result;
else
r e t u r n DF_Search(S, s, F , 0) w α;
} /* evalU() */

/* Compute Ps [Φ U ≤k−horizon Ψ] */
double DF_Search(PFSS S, state s, formula F, i n t horizon)
{
Let S = (S, q, A, next);

i f (BPCTL_rec(S, s, Ψ))
prob = 1.0;
e l s e i f (horizon != k && !BPCTL_rec(S, s, Φ))
prob = 0.0;
else
{
/* prob accumulates Ps [Φ U ≤k−horizon Ψ] */
prob = 0.0;
i f (horizon < k)
{
f o r e a c h (s_next, a, p_next) in n e x t (s)
{
prob_tmp = present_cache(C, s_next, F , k - horizon - 1);
i f (prob_tmp == -1) /* not found */
prob = prob + p_next*DF_Search(S,s_next,F ,horizon + 1);
else
/* here, prob tmp = Ps next [Φ U ≤k−horizon−1 Ψ], so we can use it
instead of calling DF Search */
prob = prob + p_next*prob_tmp;
i f (horizon == 0 && prob w α)
r e t u r n prob;
} /* foreach */
} /* if */
} /* else */
/* s exploration ended */
insert_cache(C, s, F , k - horizon, prob);
r e t u r n prob;
} /* DF_Search() */

Figure 4.2: Functions evalU and DF Search


4.1 Explicit BPCTL Model Checking 47

/* Check if s |= F using only the cache, where F is a


U-subformula of the formula being verified */
(bool , b o o l ) try_to_evaluate(Cache C, state s, formula F )
/* The first bool returned is true iff it was possible to
evaluate s |= F by using only the cache; if the first bool
is true, then the second is true iff s |= F */
{
Let F ≡ [Φ U≤k Ψ]wα ;
i f (there i s no j such that
C[j].state == s && C[j].form == F )
r e t u r n ( f a l s e , _);
/* since the first value is not true, it does not matter
which is the second one */
else
{
/* the same formula, starting from the same state, may
have been computed with different horizons */
f o r e a c h j such that (C[j].state == s
&& C[j].form == F )
{
i f (C[j].horizon == k)
r e t u r n ( t r u e , C[j].prob w α);
e l s e i f (C[j].horizon < k && C[j].prob w α)
/* C[j].prob is computed with a smaller horizon, but is
already grater than α */
r e t u r n ( t r u e , t r u e );
e l s e i f (C[j].horizon>k && C[j].prob 6w α)
/* C[j].prob is computed with a larger horizon, but is still
smaller than α */
r e t u r n ( t r u e , f a l s e );
} /* foreach */
/* unable to evaluate */
r e t u r n ( f a l s e , _);
} /* else */
} /* try_to_evaluate() */

Figure 4.3: Function try to evaluate


48 Chapter 4. Verification of BPCTL Properties

needs to reach the horizon (i.e. h = k − j, being j the number of steps from s to t). Thus, we avoid to
perform already done computations, so saving time with a fixed amount of memory.

This saving may take place in two ways:

• when evalU calls try to evaluate (see Figure 4.3), to attempt to avoid a call to DF Search.
The idea here is that, since Ps [Φ U≤h1 Ψ] ≤ Ps [Φ U≤h2 Ψ] when h1 < h2 (see Lemma 4.2), then
even if the searched pair (state, formula) is not present in the cache with the required horizon, we
may be able to say if s |= F (see Example 4.3);

• when DF Search calls present cache, to attempt to avoid a recursive call. In this case, the
recursive call can be avoided only if we find in the cache the slot with the exact state, formula and
horizon, since here we need the exact probability value. Example 4.2 shows a case in which this
happens.

Finally, function insert cache, being slightly different from the usual implementation, is shown in
Figure 4.4. When a free cache slot has not been found, a slot is overwritten only if it refers to the same pair
(state, formula), and has a smaller horizon. In this way, we overwrite only information obtained with less
computations.

/* Store values in C */
v o i d insert_cache(Cache C, state s, formula F , i n t hor, double
prob)
{
/* standard hashing function */
count = 0;
h = hash(s, count);
w h i l e (C[h] 6= ∅ && count <= max_chain_length)
{
/* this loop searches a free slot among the ones that
correspond to the hash values of s */
count = count + 1;
h = hash(s, count);
} /* while */
i f (C[h] == ∅)
/* ok to insert in the slot h */
C[h] = (s, F , hor, prob);
else
{
i f (C[h].state == s && C[h].form == F &&
C[h].horizon < hor)
/* overwrite the previous contents: since hor is greater
than C[h].horizon, prob is harder to recompute than
C[h].prob */
C[h] = (s, F , hor, prob);
} /* else */
} /* insert_cache() */

Figure 4.4: Function insert cache


4.1 Explicit BPCTL Model Checking 49

4.1.2 E XAMPLES

We now illustrate how our algorithm works by means of three simple examples. An animated presentation
of these examples may be found in [50].

Example 4.1 illustrates only the basic mechanism of the DF visit, when carried out on a U-formula without
nested U-formulas. To this end, we consider a version of the algorithm which does not use the cache;
Example 4.2 deals with the usage of the cache in function DF Search. Here, caching is useful for every
type of U-formula;
Example 4.3 shows how the cache is used by function try to evaluate. Here, caching is useful only
with nested U-formulas.

Finally, in all the examples, states are labeled with their visiting order in the DF visit, and we denote with
probsi the final value for the variable prob when DF Search is called on the state s i .
Example 4.1. Suppose we are given the simple PFSS S of Figure 4.5 and the BPCTL formula F ≡
[tt U≤2 φ]≥0.6 , where φ ∈ is an atomic proposition such that φ(s1 ) = φ(s4 ) = φ(s7 ) = 1 and is 0
on the other states (as shown in Figure 4.5). We want to verify if s0 |= F . In this example, we will execute
the algorithm in Figure 4.9 (supposing that the call to function evalU made by BPCTL rec is replaced
by the call to evalU nC, with the same parameters). This allows us to focus on the basic mechanism of
the algorithm, since we do not use the cache.

Then, the computation of probs0 is performed in the following way:

1 1
probs0 → probs1 →
3 3

1 1 1 11 1 11 1 11
→ + probs2 → + probs3 → + probs4 → + (4.1)
3 3 3 32 3 32 3 32

1 1 1 1 1
→ + probs5 → + 1probs6 →
2 3 2 3 2

In fact, probs3 = probs6 = 0, since s3 and s6 are reached with horizon = 2 and they do not satisfy
φ. On the other hand, probs1 = probs4 = 1, since φ(s1 ) = φ(s4 ) = 1. Moreover, s7 and s8 are never
reached, since s7 is only reachable from s1 , that is not expanded since φ(s1 ) = 1, while s8 is beyond the F
horizon, i.e. it is not reached in at most 2 steps from s0 . So, the final result is that s0 6|= F , since 0.5 6≥ 0.6.

Note that, if the formula were F ≡ [tt U≤2 φ]≥0.5 , i.e. if the probability bound was 0.5 instead of 0.6, then
neither s5 nor s6 would have been visited. In fact, the above computation would have returned true at the
end of (4.1), since 13 + 31 12 = 0.5 ≥ 0.5.
Example 4.2. Suppose we are given the PFSS S partially shown in Figure 4.6 and the BPCTL formula
F ≡ [tt U≤k φ]≥0.9 , where k > 3 and φ ∈ AP is an atomic proposition which is false in all the states
shown in Figure 4.6 (i.e., ∀0 ≤ i ≤ 6 φ(si ) = 0). As in Example 4.1, we want to verify if s0 |= F .
However, here we will use also the cache, and we suppose that the return prob; statement at the end
of the foreach loop in DF Search is never executed.

We now focus on state s3 , which is reached for the first time as successor of s2 . Then, probs3 is computed,
and the 4-tuple (s3 , F, k − 3, p) is put on the cache, being p = probs3 . When s3 is reached again as
50 Chapter 4. Verification of BPCTL Properties

1 1 s8
s6
1
3 1
s5
s4 1
1
1 2 φ
3
s0 s2
1
2 1
s3
1
3 s1
φ
s7 1
1 φ

Figure 4.5: The PFSS of Example 4.1

successor of s4 , a recursive visit of s3 should be performed to compute again probs3 . However, the repe-
tition of the DF visit of s3 is avoided, since present cache(C,s3 , F, k − 3) returns p (supposing that the
corresponding slot has not been overwritten in the meanwhile). Thus, the DF visit of s 4 can directly use p
to compute probs4 .

In a similar way, it is possible to avoid another DF visit of S starting from s 4 (when s4 is reached again as
a successor of s5 ), as well as the third one starting from s3 (when s3 is reached again as a successor of s6 ).
1 1
2 2 1
s5 s6
...
1
2
1 ..
s0 s4 s3 .
1
2
1 1
...
2 2 1
s1 s2

Figure 4.6: The PFSS of Example 4.2

Example 4.3. Suppose we are given the simple PFSS S of Figure 4.7 and the BPCTL formula Φ g ≡
[tt U≤2 Φn ]≤0 , where Φn ≡ [tt U≤2 φ]≥1 is a U-subformula and φ ∈ AP is an atomic proposition such
that φ(s4 ) = 1 and ∀s 6= s4 φ(s) = 0 (as shown in Figure 4.7). As in Example 4.2, we want to verify
if s0 |= F , and we again suppose that the return statement at the end of the foreach statement in
DF Search is never executed.

Differently from Examples 4.1 and 4.2, we now have to verify a U-formula Φ g with a nested U-subformula
Φn . This entails that, during the DF visit of S to compute Ps0 [Φg ], other nested DF visits are needed to
determine if the states s reachable from s0 verify s |= Φn :

1. DF Search(S, s0 , Φn , 0) inside the call DF Search(S, s0 , Φg , 0);

2. DF Search(S, s1 , Φn , 0) inside the call DF Search(S, s1 , Φg , 1);

3. DF Search(S, s2 , Φn , 0) inside the call DF Search(S, s2 , Φg , 2);

4. DF Search(S, s3 , Φn , 0) inside the call DF Search(S, s3 , Φg , 2);

5. DF Search(S, s3 , Φn , 0) inside the call DF Search(S, s3 , Φg , 1);

6. DF Search(S, s4 , Φn , 0) inside the call DF Search(S, s4 , Φg , 2);


4.1 Explicit BPCTL Model Checking 51

The calls 1, 2, 3 and 6 are unavoidable, and are effectively performed (however, the call 6 immediately
returns 1, since φ(s4 ) = 1). These four DF visits also put onto the cache the final values they compute.
Figure 4.8 show the evolution of the cache contents, with the simplifying assumption that all insertions are
done in successive slots. Namely, call 1 inserts slots C[0] through C[4], while call 2 inserts slots C[5] through
C[7]; finally, call 3 inserts the slot C[8].

The calls 4 and 5 are instead avoided. In fact, consider the call to evalU(S, s 3 , Φn ), which should execute
the call 4. However, before this latter call is made, try to evaluate(C,s 3 , Φn ) is invoked, with the 4-
tuple C[7] = (s3 , Φn , 1, 1.0) present on the cache (see Figure 4.8). Here, function try to evaluate
returns true since, even if C[7].prob = 1 is computed with a smaller horizon than the required one
(C[7].horizon = 1 < 2), we have that C[7].prob is already in the right relation (≥) with the probability
bound (1) present in the formula Φn . In the same way, the successive call to try to evaluate(C,s 3 , Φn ),
using the same cache slot C[7], avoids the call 5.
1
2
1 1
s0 s3 s4
φ
1
2
1 1
2 2
s1 s2 1

Figure 4.7: The PFSS of Example 4.3

0 s0 , Φn , 2, 12
1 s1 , Φn , 1, 0.0
2 s2 , Φn , 0, 0.0
3 s3 , Φn , 1, 1.0
4 s4 , Φn , 0, 1.0 At the end of DF Search(S, s0 , Φn , 0)
5 s1 , Φn , 2, 12
6 s2 , Φn , 1, 0.0
7 s3 , Φn , 1, 1.0 At the end of DF Search(S, s1 , Φn , 0)
8 s2 , Φn , 2, 0.0 At the end of DF Search(S, s2 , Φn , 0)
9 s2 , Φg , 0, 0.0 At the end of DF Search(S, s2 , Φg , 2)
10 s3 , Φg , 0, 1.0 At the end of DF Search(S, s3 , Φg , 2)
11 s1 , Φg , 1, 12 At the end of DF Search(S, s1 , Φg , 1)
12 s3 , Φg , 1, 1.0 At the end of DF Search(S, s3 , Φg , 1)
13 s0 , Φg , 2, 34 At the end of DF Search(S, s0 , Φg , 0) (final)

Figure 4.8: Cache evolution for Example 4.3

4.1.3 A LGORITHM C ORRECTNESS

Here we give a correctness proof of the algorithm shown in Section 4.1.1. We begin by proving the correct-
ness of the algorithm without the cache usage, so dealing with functions evalU nC and DF Search nC
of Figure 4.9 (we also suppose, of course, that BPCTL rec calls evalU nC instead of evalU).

We start with two general purpose lemmas.


52 Chapter 4. Verification of BPCTL Properties

b o o l evalU_nC(PFSS S, state s, formula F )


{
Let F ≡ [Φ U≤k Ψ]wα ;

r e t u r n DF_Search_nC(S, s, F , 0) w α;
} /* evalU_nC() */

double DF_Search_nC(PFSS S, state s, formula F , i n t horizon)


{
i f (BPCTL_rec(S, s, Ψ))
prob = 1.0;
e l s e i f (horizon!=k && !BPCTL_rec(S,s,Φ))
prob = 0.0;
else
{
prob = 0.0;
i f (horizon < k)
{
f o r e a c h (s_next, a, p_next) in n e x t (s)
prob = prob + p_next*DF_Search_nC(S,s_next,F ,horizon+1);
i f (horizon == 0 && prob w α)
r e t u r n prob;
} /* if */
} /* else */
r e t u r n prob;
} /* DF_Search_nC() */

Figure 4.9: Functions evalU nC and DF Search nC


4.1 Explicit BPCTL Model Checking 53

Lemma 4.1. Let S = (S, q, A, next) be a PFSS, and let Φ, Ψ ∈ LBP CT L be BPCTL formulas. Then, for
all s ∈ S such that s 6|= Ψ and s |= Φ, and for all i ∈ N such that i ≥ 1, we have that

X
Ps [Φ U ≤i Ψ] = ph Psh [Φ U ≤i−1 Ψ]
(sh ,ah ,ph )∈next(s)

Proof. Let Π(s, Φ, Ψ, i) =def {π ∈ Path(S, s) | π |= Φ U ≤i Ψ}. Let next(s) = {(s1 , a1 , p1 ), . . . ,


(sn , an , pn )}. Then,

X
Ps [Φ U ≤i Ψ] = P(π) =
π∈Π(s,Φ,Ψ,i)

X X i−1
Y
P(π(j), π(j + 1)) =
(sh ,ah ,ph )∈next(s) π ∈ Π(s, Φ, Ψ, i) j=0
π(1) = sh

X X i−1
Y
P(π(0), π(1)) P(π(j), π(j + 1)) = (4.2)
(sh ,ah ,ph )∈next(s) π ∈ Π(s, Φ, Ψ, i) j=1
π(1) = sh

X X
ph P(π) = (4.3)
(sh ,ah ,ph )∈next(s) π∈Π(sh ,Φ,Ψ,i−1)

X
ph Psh [Φ U ≤i−1 Ψ]
(sh ,ah ,ph )∈next(s)

Note that, in the passage from (4.2) to (4.3), we have used the hypothesis that π(0) 6|= Ψ and π(0) |= Φ.

Lemma 4.2. Let S = (S, q, A, next) be a PFSS, Φ, Ψ ∈ LBP CT L be BPCTL formulas, s ∈ S be a state
and h1 < h2 ∈ N be two integers. Then,

Ps [Φ U≤h1 Ψ] ≤ Ps [Φ U≤h2 Ψ]

Proof. By Definition 2.11, we have to prove that Prob{π ∈ Path(S, s) | π |= Φ U ≤h1 Ψ} ≤ Prob{π ∈
Path(S, s) | π |= Φ U≤h2 Ψ}, which can be done by showing that {π ∈ Path(S, s) | π |= Φ U≤h1 Ψ} ⊆
{π ∈ Path(S, s) | π |= Φ U≤h2 Ψ}. So, we have to prove that, if π |= Φ U≤h1 Ψ, then π |= Φ U≤h2 Ψ,
being π ∈ Path(S, s). By contradiction, suppose that π |= Φ U≤h1 Ψ and π 6|= Φ U≤h2 Ψ; by
Definition 2.9, this is equivalent to say that ∃h ≤ h1 : (π(h) |= Φ2 and ∀i < h π(i) |= Φ1 ) and
∀h ≤ h2 (π(h) 6|= Φ2 or ∃i < h π(i) 6|= Φ1 ). Since h1 < h2 , these two statements are in contradiction.

We now give two other useful and more specific lemmas.


54 Chapter 4. Verification of BPCTL Properties

Lemma 4.3. Suppose that function DF Search nC is called with parameters S = (S, q, A, next), s ∈ S,
F ≡ [Φ U ≤k Ψ]wα and 0. Then, a state t ∈ S is explored by function DF Search nC iff t is reachable
from s in at most k steps.

Proof. Suppose that a state t is reachable from s in at most k steps. This implies that it exists a path
π = s0 s1 . . . sh such that s0 = s, sh = t and h ≤ k. This in turns entails that, for all 0 ≤ i < h,
there exist a ∈ A and p ∈ (0, 1] such that next(si ) 3 (si+1 , a, p). Thus, the foreach loop of function
DF Search nC ensures that, for all i, DF Search nC is recursively called on si , and finally on t.

On the other hand, let t be a state explored by DF Search nC, and let h ∈ N
be the corresponding value of horizon. Thus, there must be a chain of calls
DF Search nC(S, s0 , F, 0), . . . , DF Search nC(S, sh , F, h), where s0 = s, sh = t and h ≤ k (the
last statement holds since the recursive call is made only if horizon < k). However, this calls chain may
take place only if, for all 0 ≤ i < h, there exist a ∈ A and p ∈ (0, 1] such that next(s i ) 3 (si+1 , a, p).
This implies that t is reachable from s in at most k steps.

Lemma 4.4. Suppose that function DF Search nC is called with parameters S = (S, q, A, next), s ∈ S,
F ≡ [Φ U ≤k Ψ]wα and 0. Moreover, suppose that, for all t ∈ S that are reachable in k steps from s,
BPCTL rec(S, t, Φ) and BPCTL rec(S, t, Ψ) return true iff, respectively, t |= Φ and t |= Ψ. Then, this
call to DF Search nC will return Ps [Φ U ≤k Ψ], or a value p such that Ps [Φ U ≤k Ψ] ≥ p w α.

Proof. The proof is carried out in two steps. Initially, we assume that the last statement of the foreach
loop in function DF Search nC (i.e., return prob) is never executed, so the DF visit is never
stopped before its termination; under this assumption, we show that DF Search nC will always return
Ps [Φ U ≤k Ψ]. In the second step we relax this assumption, so proving the whole Lemma. Moreover, in all
the proof we suppose that all the calls to function BPCTL rec made in DF Search nC always return the
correct value, since this is implied by Lemma 4.3 and by the lemma hypothesis.

Thus, we now show that, if DF Search nC is called with parameters S, s, F and n, then it returns
Ps [Φ U ≤k−n Ψ]. Once we have showed this, we have obtained also the thesis since, in the first call, n
is set to 0. The proof of such a statement is made by induction on k − n, which take values in the interval
from 0 (at the end of the recursive calls chain, when DF Search nC is called with n = k) to k (in the first
call to DF Search nC, when n = 0).

For the recursion base we have to show that function DF Search nC, when called with parameters S,
s ∈ S, F and n = k (so k − n = 0), returns Ps [Φ U ≤0 Ψ]. By Definitions 2.9 and 2.11, we have that
Ps [Φ U ≤0 Ψ] = Prob{π ∈ Path(S, s) | π |= Φ U ≤0 Ψ} = Prob{π ∈ Path(S, s) | s |= Ψ}. So, if s |= Ψ,
we have that Ps [Φ U ≤0 Ψ] = 1, otherwise Ps [Φ U ≤0 Ψ] = 0. Indeed, function DF Search nC returns 1
or 0, depending on the evaluation of s |= Ψ (no recursive call is made in this case).

For the induction step we know that, if DF Search nC is called with parameters S, s ∈ S, F and n ≤ k,
then it returns Ps [Φ U ≤k−n Ψ]. Suppose now that function DF Search nC is called with parameters S,
s ∈ S, F and n − 1; thus, we have the following cases:

1. If s |= Ψ, then by Definitions 2.9 and 2.11, for all h ∈ N, Ps [Φ U ≤h Ψ] = Prob{π ∈


Path(S, s) | π |= Φ U ≤h Ψ} = Prob{π ∈ Path(S, s) | true} = 1; in particular,
Ps [Φ U ≤k−n+1 Ψ] = 1, and in fact DF Search nC returns 1.

2. If s 6|= Ψ and s 6|= Φ, we analogously have that Ps [Φ U ≤k−n+1 Ψ] = 0, and in fact DF Search nC
returns 0.
4.1 Explicit BPCTL Model Checking 55

3. Otherwise, if s 6|= Ψ andX


s |= Φ, then we are in the hypothesis of Lemma 4.1, so
Ps [Φ U ≤k−n+1 Ψ] = ph Psh [Φ U ≤k−n Ψ]. Since, by induction hypothesis, the
(sh ,ah ,ph )∈next(s)
recursive calls to DF Search nC with parameters S, shX
such that (sh , ah , ph ) ∈ next(s), F and n
correctly return Psh [Φ U ≤k−n Ψ], we have that ph Psh [Φ U ≤k−n Ψ] is correctly
(sh ,ah ,ph )∈next(s)
returned.

Now, suppose that the return prob statement inside the foreach loop in function DF Search nC
(we call I this statement) is eventually executed. If I were not executed, then the final value of prob would
have been Ps [Φ U ≤k Ψ]. However, since prob is always incremented until it reaches this value, the value
of prob when I is executed will be p such that Ps [Φ U ≤k Ψ] ≥ p w α.

Now we give the main correctness theorem for the algorithm without cache.

Theorem 4.5. Let F ∈ LBP CT L be a BPCTL formula to be verified in a PFSS S = (S, q, A, next). Then,
BPCTL(S, F ) (where calls to evalU are replaced by calls to evalU nC) returns true iff S |= F .

Proof. Since S |= F iff q |= F in the Markov Chain S mc , to prove this theorem it is sufficient to show that,
for all s ∈ S, BPCTL rec(S, s, F ) returns true iff s |= F .

The proof is by structural induction on F . Thus, we assume that ∀s ∈ S BPCTL(S, F i ) returns true iff
s |= Fi is true for all subformulas Fi of F , and we show that ∀s ∈ S BPCTL(S, F ) returns true iff s |= F .

As induction basis, note that if F ≡ tt or F ≡ p ∈ AP (i.e., F does not contain any subformula), then
true and p(s) is correctly returned, respectively. For the other cases, we have:

• if F ≡ F1 ∧ F2 , then true is correctly returned iff s |= F1 and s |= F2 (in fact, by induction


hypothesis, BPCTL rec(S, s, F1 ) return true iff s |= F1 and analogously for F2 );

• F ≡ [XΦ]wα , then function evalX is called. By induction hypothesis, for all s h , the recursive calls
BPCTL rec(S, sh , Φ) made by evalX return true iff sh |= Φ, so we have that Xfunction evalX
computes, if the return true; statement is never executed, sum = ph =
(sh ,ah ,ph )∈next(s) and sh |=Φ
Prob{π ∈ Path(S, s) | π(1) |= Φ} = Ps [X Φ]. Since sum 6w α, false is correctly re-
turned. Otherwise, if the return true; statement is eventually executed, then we have that
Ps [X Φ] ≥ sum w α, so true is correctly returned.

• F ≡ [Φ U≤k Ψ]wα , then function DF Search nC is initially called with parameters S, s, F and 0.
Since, by induction hypothesis, for all s ∈ S, BPCTL rec(S, s, Φ) (resp. BPCTL rec(S, s, Ψ)) return
true iff s |= Φ (resp. s |= Ψ), we are in the hypothesis of Lemma 4.4, so DF Search nC returns
Ps [Φ U≤k Ψ] or a value p such that Ps [Φ U ≤k Ψ] ≥ p w α. In both cases, after the comparison with
α made in function evalU, we have that BPCTL rec will correctly return true iff s |= F .

Now, we are ready to prove the correctness of the whole algorithm. Before proving the corresponding
theorem, we have to give a lemma on the cache contents properties.
56 Chapter 4. Verification of BPCTL Properties

Lemma 4.6. Let c = (s, F, h, p) be a slot of the cache C, at any execution time. Then, if F ≡ [Φ U ≤k Ψ]wα ,
we have that

p = Ps [Φ U ≤h Ψ] (4.4)

Proof. The only function inserting values in the cache is DF Search, which performs the insertion as
the last but one statement. Let us consider the call to DF Search which inserts c in C. To demonstrate
the Lemma it is sufficient to prove that the value of variable prob, immediately before the returning of
function DF Search, is exactly p = Ps [Φ U ≤h Ψ]. This is done by induction on the number i of elements
currently stored in C.

As induction basis, we prove that, if C does not contain any 4-tuple, then the value of prob is p =
Ps [Φ U ≤h Ψ]. This is true by Lemma 4.4, since in this case the cache is not used to determine the
value of prob (each call to present cache returns −1).

As induction step, suppose that there are i tuples in C, each satisfying (4.4). This implies that, in the current
call to DF Search, if present cache returns a value v 6= −1, then v will be the same returned by a
call to function DF Search nC. Thus, DF Search has the same behavior as DF Search nC and, by
Lemma 4.4, it will return the same p = Ps [Φ U ≤h Ψ].

Now, we can give our main theorem.


Theorem 4.7. Let F ∈ LBP CT L be a BPCTL formula to be verified in a PFSS S = (S, q, A, next). Then,
BPCTL(S, F ) returns true iff S |= F .

Proof. Since Theorem 4.5 guarantees the correctness of the algorithm without the cache, we only have to
prove the correctness of the caching strategy. To this end, we now analyze in detail the only two functions
in which the cache is used.

DF Search By Lemma 4.6 and its proof, it returns the same values of DF Search nC; by Theorem 4.5,
this function works correctly.
evalU Here we have that some calls to DF Search are avoided, via function try to evaluate. To
prove that this does not affect the algorithm correctness, consider a generic call to evalU(S, s, F ),
with F ≡ [Φ U ≤k Ψ]wα , and suppose that there are n indices i1 , . . . , in such that, for all
j = 1, . . . , n, C[ij ].state = s and C[ij ].form = F . By Lemma 4.6, we have that C[ij ].prob =
Ps [Φ1 U≤C[ij ].horizon Φ2 ] for all j = 1, . . . , n. Now, if n = 0 or none of the C[ij ] satisfy one of
the three conditions inside the foreach loop of try to evaluate, then evalU will have the
same behavior of evalU nC, so by Theorem 4.5 the thesis is true. Otherwise, let i j be the first index
such that C[ij ] satisfy one of the three conditions inside the foreach loop of try to evaluate.
Then, we have one of these three cases:

1. C[ij ].horizon = k; in this case, Ps [Φ1 U≤k Φ2 ] = C[ij ].prob w α is correctly returned.
2. C[ij ].horizon < k and (C[ij ].prob w α); in this case, by Lemma 4.2 we have that
Ps [Φ1 U≤k Φ2 ] ≥ Ps [Φ1 U≤C[ij ].horizonΦ2 ] = C[ij ].prob w α, so true is correctly returned.
3. C[ij ].horizon > k and ¬(C[ij ].prob w α); in this case, by Lemma 4.2 we have that
Ps [Φ1 U≤k Φ2 ] ≤ Ps [Φ1 U≤C[ij ].horizon Φ2 ] = C[ij ].prob @ α, so false is correctly re-
turned (supposing that @ is the symbol < when w is ≥, and ≤ when w is >).
4.1 Explicit BPCTL Model Checking 57

4.1.4 A LGORITHM I MPLEMENTATION

In this section we sketch our implementation of the algorithm given in Section 4.1.1 within the FHP-Murϕ
verifier described in Section 3.2.3. As in Section 3.2.3, we do not deal with the changes to Murϕ input
language which are needed to define PFSSs and BPCTL formulas, since these details may be found in
Appendix A.

The implementation of the verification algorithm follows the lines shown in Figures 4.1, 4.2, 4.3 and 4.4.
The only adjustment is in function DF Search, which is not implemented using standard C recursion,
since this should be not efficient. Thus, a stack has been implemented to explicitly handle the recursive
calls, by properly modifying the implementation of the DF visit in the original Murϕ. Since we are in a
bounded framework, the stack size is limited as follows.
Definition 4.1. Let stack size: LBP CT L → N be the function returning the stack size (i.e. the number of
stack entries) that is needed to verify a BPCTL formula Φ. Then stack size is defined as follows:

• stack size(tt) = stack size(p) = 0;


• stack size(Φ1 ∧ Φ2 ) = max{stack size(Φ1 ), stack size(Φ2 )};
• stack size([XΦ]wα ) = stack size(¬Φ) = stack size(Φ);
• stack size([Φ1 U≤k Φ2 ]wα ) = max{stack size(Φ1 ), stack size(Φ2 )} + k.

Hence, the amount of memory needed by the verification task is fixed, so we have that our real bottleneck
is time, and not memory. However, to handle the case in which we need more memory than the available
one, we implemented the swap-to-disk mechanism stack cycling, which is also implemented in the DF-
based verifier SPIN [34]. With this technique, only a part of the stack is maintained in By Definition
2.9, it is clear that the most difficult case in the verification of a BPCTL formula is to compute the truth
value of U-subformulas. This mechanism avoids too frequent disk accesses due to alternation of push-pop
operations (thrashing). For exposition completeness, we show in Figure 4.10 the pseudocode of push and
pop operations, and of the related functions.

In this way, we use RAM to store the cache and part of the DF stack. Our experiments show that typically
we can take the RAM size for the DF stack as inversely proportional to the number of nested U-formulas
(however, never more than half memory has been dedicated to the stack). In fact, the more nested U-
formulas, the larger the cache, in order to speed up the verification process.

With respect to the algorithm described in Figures 4.1, 4.2, 4.3 and 4.4, our implementation introduces three
minor enhancements:

• We develop a mechanism allowing to terminate a DF visit before its “natural” termination, while
maintaining the correctness of the value returned by evalU. The idea is that we want to stop a DF
visit exactly when the computed probability becomes greater than the given threshold. This is not
what function DF Search in Figure 4.2 does, since it waits for the backtracking to level 0. Our
mechanism, instead, maintains the probability at the top level (i.e. at level 0) in a separate variable
prob 0. To this end, we maintain also a variable prob path storing the probability of the path
from q to the current state (this path could also be obtained by chaining the states on the current DF
stack). Then, prob 0 is incremented by prob path every time that prob is incremented. After
every prob 0 updating, we check if prob 0 is greater than the probability bound; if this is true, the
search can safely be terminated.
• Moreover, we modify function insert cache of Figure 4.4 in order to take into account the case
in which the same pair (state, BPCTL formula) has to be inserted in the cache with different horizons
58 Chapter 4. Verification of BPCTL Properties

v o i d init_stack(stack S, i n t mem_avail, i n t stack_length)


{
S.top = S.dsk2stck_cnt = S.stck2dsk_cnt = 0;
i f (mem_avail >= stack_length*mem_for1entry)
/* disk not needed, will never be used */
S.size = stack_length;
else
{ /* mem_avail bytes of RAM will be used */
S.size = mem_avail/mem_for1entry;
round off by defect S.size to an even number;
} /* else */
allocate S.size entries for S.entries;
} /* init_stack() */

v o i d push(stack S, entry_type entry)


{
S.entries[S.top++] = entry;
i f (S.top >= S.size) /* too many push, disk required */
Stack2Disk();
} /* push() */

entry_type pop(stack S)
{
i f (stck2dsk_cnt != dsk2stck_cnt && top == 0)
/* too many pop, disk required */
Disk2Stack();
r e t u r n S.entries[--S.top];
} /* pop() */

/* Store on disk the first S.size/2 stack entries */


v o i d Stack2Disk(stack S)
{
S.stck2dsk_cnt++;
write on disk the first half of S.entries;
move the second half of S.entries in its first half;
S.top = S.size/2;
} /* Stack2Disk() */

/* Retrieve the last S.size/2 stack entries from the disk */


v o i d Disk2Stack(stack S)
{
S.dsk2stck_cnt++;
move the first half of S.entries in its second half;
move the disk file pointer back of S.size/2 entries;
copy from the disk file S.size/2 entries into the first half
of S.entries;
reset the disk file pointer to the position preceding the read
operation;
S.top = S.size/2;
} /* Disk2Stack() */

Figure 4.10: Push and pop operations in the stack cycling mechanism
4.2 Experimental Results 59

(and probabilities). In fact, in this case, if these pairs are more than max chain length, some
of these pairs will be surely overwritten, since the while statement of function insert cache
is bounded to max chain length iterations. To avoid this, we simply augment the maximum
collision chain (i.e., max chain length in Figure 4.4). Our experiments on this topic show that
a good policy is to duplicate max chain length when it happens too frequently that a free slot
is not found. Thus, if n is the number of calls to insert cache and k is the number of times
that the while fails in finding a free slot (i.e., it ends with count = max chain length + 1), then
max chain length is duplicated if nk > β. In our experiments, we always set β = 0.05.
• Finally, our implementation deals with all the possible comparisons between a path formula prob-
ability and a real number, so we can verify properties of the type [·] <α or [·]≤α . We will use also
properties of this type in our experiments.

4.2 E XPERIMENTAL R ESULTS

In this section, our goal is to show the effectiveness of our approach; to this end, we run two kinds of
experiments.

First, in Section 4.2.1, we compare verifications of BPCTL formulas done by FHP-Murϕ with verifications
of the same models and formulas done by the probabilistic model checker PRISM [52].

Second, in Section 4.2.2, we run FHP-Murϕ to verify a robustness property on a quite large probabilistic
hybrid system.

4.2.1 P ROBABILISTIC D INING P HILOSOPHERS

In this section we give our experimental results on using FHP-Murϕ on the probabilistic protocols included
in PRISM distribution [52]. As we did in Section 3.3.1, we do not consider the protocols that lead to
Markov Decision Processes or to Continuous Time Markov Chains, since FHP-Murϕ cannot directly deal
with them. Hence we only consider Pnueli-Zuck [51] and Lehmann-Rabin [45, 46] probabilistic dining
philosophers protocols. For both of these protocols, we use two versions: the one which can be found in
the PRISM distribution, and the modified version allowing quality of service properties verifications, as it
is described in Section 3.3.1.

For what concerns the BPCTL properties to be verified, we proceed as follows. The BPCTL properties in
the PRISM distribution about these protocols are of the type ΦP ≡ φ → [tt U≤k ψ]≥α , where φ and ψ are
atomic propositions. However, these formulas are not evaluated on the initial state (which is the standard
PCTL semantics), but on all reachable states. To obtain a comparable result with FHP-Murϕ, we verify the
property ΦM ≡ [tt U≤d ¬ΦP ]≤0 ≡ [tt U≤d (φ ∧ ¬[tt U≤k ψ]≥α )]≤0 , where d is the diameter of the
protocol state space, i.e. the length of the maximum path between two states. In this way, we have that
q |= ΦM (where q is the system initial state) iff, for all reachable states s, s |= ΦP .

Our results are in Tables 4.1 and 4.2. In these experiments, we always set k = 20, and we use a machine
with 2 processors (both INTEL Pentium III 500Mhz) and 2GB of RAM. The fields in Tables 4.1 and 4.2
are straightforward, although it is necessary to remember that NPHIL denotes the number of philosophers
(obviously, the larger NPHIL, the larger the number of reachable states). For what concerns the verification
options, we leave the default options for PRISM, and we give the option -m500 to FHP-Murϕ, to force it
to use exactly 500 MB of RAM.

Note that, in these set of experiments, which do not involve mathematical operations, PRISM works better
than FHP-Murϕ, which takes more time to complete the verifications.
60 Chapter 4. Verification of BPCTL Properties

NPHIL Result Murϕ Mem PRISM Mem Murϕ Time PRISM Time
5 false 5.0e+2 1.70e+00 5.45e+01 2.00e+00
6 false 5.0e+2 1.43e+01 2.86e+02 1.26e+01

Table 4.1: Experimental results for the verification of a BPCTL property on the Pnueli-Zuck protocol as it
is found in the PRISM distribution. The experiments were carried out on a machine with 2 processors, both
Pentium III 500MHz, with 2GB of RAM. Time is in seconds, memory occupations are in MB.

NPHIL Result Murϕ Mem PRISM Mem Murϕ Time PRISM Time
3 true 5.0e+2 1.42e+00 1.81e+03 2.43e+00
4 true 5.0e+2 2.36e+01 1.25e+05 1.25e+02

Table 4.2: Experimental results for the verification of a BPCTL property on the Lehmann-Rabin protocol
as it is found in the PRISM distribution. The experiments were carried out on the same machine of Table
4.1. Time is in seconds, memory occupations are in MB.

NPHIL MAX WAIT Result Murϕ Mem PRISM Mem Murϕ Time PRISM Time
5 3 true 5.0e+2 9.17e+02 1.81e+03 2.75e+03
5 4 true 5.0e+2 N/A 1.79e+03 N/A

Table 4.3: Experimental results for the verification of a BPCTL property on the Pnueli-Zuck protocol as it
was modified in Section 3.3.1. The experiments were carried out on the same machine of Table 4.1. Time
is in seconds, memory occupations are in MB.

NPHIL MAX WAIT Result Murϕ Mem PRISM Mem Murϕ Time PRISM Time
3 4 true 5.0e+2 7.01e+01 1.17e+03 6.37e+02
4 3 true 5.0e+2 N/A 3.21e+04 N/A

Table 4.4: Experimental results for the verification of a BPCTL property on the Lehmann-Rabin protocol
as it was modified in Section 3.3.1. The experiments were carried out on the same machine of Table 4.1.
Time is in seconds, memory occupations are in MB.
4.2 Experimental Results 61

For the modified version of the probabilistic dining philosophers, we verify a reliability property. In fact, in
these models there is a set o f error states, i.e. those satisfying a special atomic proposition φ err . Namely,
φerr could be informally stated as “a philosopher does not eat for a too long time, and dies for starvation”.
To define our reliability property, we introduce a new atomic proposition φ und , which is a weaker version
of φerr . This means that, for all states s, if s satisfies φerr , then it satisfies also φund (informally, “a
philosopher does not eat for a long time, and he is in danger”). So, our undesired s tates are those satisfying
φund .

More in detail, suppose that φerr is defined by means of a constant kerr , denoting the maximum number of
times that a philosopher can fail in getting both forks before dying (in our experiments, this k err is named
MAX WAIT). On the other hand, φund is defined by means of a constant kund such that kund < kerr (in
our experiments, we always took kund = kerr − 1). That is to say, when a philosopher fails for kund
consecutively times in getting both its forks, it reaches an undesired state (in which φ und holds). Using
the Murϕ syntax, we can define φerr as exists p: phil range do (wait[p] < MAX WAIT)
endexists, while φund is exists p: phil range do (wait[p] < MAX WAIT MINOR)
endexists (where MAX WAIT MINOR is kund ).

Now, we want to say that, when an undesired state s is reached, then the system almost always reaches,
from s and in a few steps, a non-error state. Then, our property states that there is a low probability that we
reach in k1 steps a state s such that φund (s) holds, and there is not a high probability of reaching, from s
and in k2 = k101 steps, a state t such that ¬φerr (t). The corresponding BPCTL formula is [tt U≤k1 (φund ∧
¬[tt U≤k2 ¬φerr ]≥1 )]≤0 . We give this BPCTL formula both to PRISM and FHP-Murϕ.

Our results are in Tables 4.3 and 4.4. In these experiments, we always set k 1 = 20, and we use, as for
the experiments in Tables 4.1 and 4.4, a machine with 2 processors (both INTEL Pentium III 500Mhz) and
2GB of RAM. NPHIL still indicates the number of philosophers, while MAX WAIT is maximum number
of times that a philosopher can fail in getting both forks before dying. For what concerns the verification
options, we again give the option -m500 to FHP-Murϕ, to force it to use exactly 500 MB of RAM. On the
other hand, PRISM options are the default ones in the first row of Tables 4.3 and 4.4, while the “N/A” value
means that PRISM was unable to complete the verification. In this latter case, also the -m and -s options
(totally MTBDD and algebraic verification algorithm respectively) have been used, with the same failing
result.

As already said in Section 3.3.1, this is due to the fact that these protocols requires some non-trivial math-
ematical operations, that is exactly the case in which OBDDs, and therefore MTBDDs, reach their worst
memory occupation. On the contrary, mathematical operations in the computation of the transition function
next is hardly a problem for FHP-Murϕ, that succeeds in completing these verifications.

4.2.2 A “R EAL W ORLD ” P ROBABILISTIC H YBRID S YSTEM

In this section we show our experimental results on using FHP-Murϕ for the analysis of the same real
world hybrid system we described in Section 3.3.2, namely the Control System for the Gas Turbine of a
2MW Electric Co-generative Power Plant (ICARO) in operation at the ENEA Research Center of Casaccia
(Italy).

Here, we want to prove that TCS satisfies a stronger property, which is not expressible using only finite
horizon safety formulas. To this end, in this section we verify if the TCS is robust, i.e. that the following
property holds: “if TCS reaches an undesired state, then it is able to return to a non-undesired state with
an high probability”. Here, a state is considered an undesired state when it is outside a smaller safety range
than the one defining the error states. More formally, let me , Me be the constants defining the safety range,
i.e. error states are such that x ∈
/ [me , Me ], where x is the controlled parameter. Then, we introduce two
more constants md , Md , such that an undesired state is such that x ∈ [me , md ] ∪ [Md , Me ]. This models
62 Chapter 4. Verification of BPCTL Properties

the fact that the system may reach such a state, but it will crash if it stays in such a state for a too long time,
since it is close to the error bounds me , Me .

Thus, our robustness property is equivalent to state the following: there is a low probability of reaching an
undesired state s, such that there is not an high probability of reaching a non-undesired state from s. The
relative BPCTL formula is [tt U≤k1 (¬φ∧¬[tt U≤k2 φ]≥1 )]≤0.1 , where φ defines the non-undesired states,
and we suppose that the TCS is required to be robust with a 10% probability. The two constants k 1 and
k2 arelchosen inmsuch a way that k1 is sufficient to reach an undesired state (in our experiments, we took
k1 = Diam 100
(n)
100, where Diam(n) is the minimum number of states needed to reach an undesired state
k1
when MAX D U = n), and k2 is not too high (in our experiments, we took k2 = 100 ).

Our results are in Table 4.5; the first columns meaning is straightforward, while in the field “Probability” is
reported the value Pq [tt U≤k1 (¬φ∧¬[tt U≤k2 φ]≥1 )], being q the system initial state (see Definition 2.11).
Thus, the field “Probability” reports the probability of a robustness violation. As in Section 4.2.1, we used
a machine with 2 processors (both INTEL Pentium III 500Mhz) and 2GB of RAM, and give to FHP-Murϕ
the option-m500 to force it to use exactly 500 MB of RAM. The low values for the field “Probability”
indicates that TCS is indeed robust; moreover, as it was expected, the probability of a failure increases with
MAX D U.

MAX D U Visited States Rules Fired k1 CPU Time Probability


35 1.159160e+05 3.477480e+05 800 3.702400e+03 4.104681e-03
45 4.098000e+04 1.229400e+05 700 1.313900e+03 1.792883e-02
50 4.067700e+04 1.220310e+05 700 1.307850e+03 3.825000e-02

Table 4.5: Experimental results for the verification of a BPCTL property on the Turbogas Control System.
The experiments were carried out on a machine with 2 processors, both Pentium III 500MHz, with 2GB of
RAM. Time is in seconds.
C HAPTER 5

FHP-M URϕ L ANGUAGE E XPRESSIVENESS

In this chapter we investigate some of the properties of the FHP-Murϕ input language, by using two math-
ematical models of the language itself:

PFSS introduced in Definition 2.6; rather than a model of the language, it is a model of the structure used
during the verification phase;
PRBTS introduced in Section 5.2 (Definition 5.1); it is indeed a model of the FHP-Murϕ input language.

As expected, these two models have the same expressive power, since both give rise to Markov Chains (Def-
initions 2.7 and 5.3), and are both able to describe every Markov Chain, as will be proved in Propositions
5.1 and 5.3.

This Chapter is organized as follows: in Section 5.1 it is shown that every Markov Chain can be described by
means of a PFSS, while the remaining part if the chapter is dedicated to PRBTS. More in detail, in Section
5.2 we introduce the PRBTSs, showing their definition and their main properties; Section 5.3 illustrates a
possible use of PRBTSs. This Chapter ends with some examples of finite character SPs described via the
FHP-Murϕ input language (Section 5.4).

5.1 M ARKOV C HAINS AND PFSS S

In this section, we prove that each Markov Chain can be described by a PFSS; this is done in the following
proposition.
Proposition 5.1. Let M = (S, P, q) be a Markov Chain. Then it exists a PFSS S = (S, q, A, next) such
that S mc = M.

Proof. Since the set of states and the starting state are the same, we just have to build up A and next in a
way such that S mc = M. In our construction, we use a singleton as set of labels, i.e. A = {a}. We define
next in the following way: for all s ∈ S, let ti ∈ S be the states such that P(s, ti ) > 0; then, we pose
next(s) = ∪i {(ti , a, P(s, ti ))}.

Let S mc = (S, P0 , q); we now show that S mc = M. To do this, we only have to prove that P0 = P. To
this end, let s, t ∈ S be states; then, we have that P0 (s, t) = p, where p is such that (t, a, p) ∈ next(s).
However, since by construction p = P(s, t), we have the thesis.

Remark 5.1. Note that there are infinite PFSSs describing a given Markov Chain. In fact, consider again
the PFSS building in theproof
n of Propositiono
5.1. For
 example,
n two labels, i.e. A = {a 1 , a2 }, we
if we useo
i )) i ))
could pose next(s) = ∪i (ti , a1 , P(s,t
2 ∪ ∪i (ti , a2 , P(s,t
2 , and we again have S mc = M.

63
64 Chapter 5. FHP-Murϕ Language Expressiveness

5.2 P ROBABILISTIC RULE BASED T RANSITION S YSTEMS

In Proposition 2.8 we showed that it is possible to analyze finite character SPs via reducing them to an
equivalent Markov Chain. Since we know that we can describe Markov Chains in FHP-Murϕ, this allows
us to analyze finite character SPs with our tool. In this section, we want to directly show how FHP-Murϕ
input language may describe a finite character SP. To this end, we need a new mathematical model of the
FHP-Murϕ input language, in order to get more accuracy and closeness to the language than the PFSS
structure.

Here we formally define our mathematical model (called PRBTS) for the FHP-Murϕ input language, and
we show the relationships between PRBTSs and finite character SPs.

Definition 5.1. A Probabilistic Rule Based Transition System (shortened PRBTS in the following) S is a
3-tuple (S, Rules, q), where S is a finite set (of states), q ∈ S and Rules is a finite set of pairs (p, f ),
with X p being a function p : S → [0, 1] and f being a function f : S → S; moreover, for all s ∈ S,
p(s) = 1.
(p,f )∈Rules

Definition 5.2. An execution sequence (or path) in the PRBTS S = (S, Rules, q) is a nonempty finite
or infinite sequence π = s0 s1 s2 . . . where, for all i ≥ 0, there exists a pair (p, f ) ∈ Rules, such that
f (si ) = si+1 and p(si ) > 0. If π is a path, we write π(k) for the state sk , and we write π|k for the
sequence s0 s1 s2 . . . sk−1 . The length of a finite path π = s0 s1 s2 . . . sk is k, i.e. the number of transitions,
whereas the length of an infinite path is ∞; we denote with |π| the length of π.

As expected, to a PRBTS we can univocally associate a Markov Chain. This can be done as follows.

Definition 5.3. Let S = (S, Rules, q) be aX PRBTS. The Markov Chain S mc = (S, P, q) associated with
S is defined as follows: P(s, t) = p(s) (taking as 0 a summation on an empty set).
(p,f )∈Ruless.t.f (s)=t

Proposition 5.2. Let S = (S, Rules, q) be a PRBTS. Then, the Markov Chain S mc associated with S is
well defined.

Proof. To prove the thesis, we have to show that:

1. for all s, t ∈ S, P(s, t) ≥ 0; this comes from the fact that each P(s, t) is the sum of non-negative
numbers;
X X
2. for all s ∈ S, P(s, t) = 1; in fact, for all s ∈ S we have that 1 = p(s) =
t∈S (p,f )∈Rules
X X X
p(s) = P(s, t).
t∈S (p,f )∈Ruless.t.f (s)=t t∈S

Finally, the following proposition show that every Markov Chain can be described by a PRBTS; this implies,
by Proposition 5.1, that PFSS and PRBTS are equivalent formalisms.

Proposition 5.3. Let M = (S, P, q) be a Markov Chain. Then it exists a PRBTS S = (S, Rules, q) such
that S mc = M.
5.3 From Communicating Stochastic Processes to PRBTS 65

Proof. Since the set of states and the starting state are the same, we just have to build up Rules in a
way such that S mc = M. To this end, let next(s) = {t ∈ S | P(s, t) > 0} = {t1 , . . . , tNs } be,
for all s ∈ S, the set of possible successors of s. This allows us to define a function f : S × N → S
such that f (s, i) = ti is i-th state in next(s), supposing that, for i > Ns , ti = tNs 1 . Moreover, let
N = maxs∈S {Ns } = maxs∈S {|next(s)|} be the maximum number of successors for a state in S.

Then, we pose Rules = {(p1 , f1 ), . . . , (pN , fN )}, where, for all 1 ≤ i ≤ N , (pi , fi ) are defined as follows.
We pose fi (s) = f (s, i), while

(
P(s, f (s, i)) if i ≤ Ns
pi (s) =
0 otherwise

Let S mc = (S, P0 , q); we now show that S mc = M. To do this, we only have to prove that P0 = P. To this
end, let s, t ∈ S be states. If P(s, t) = 0, then t ∈
/ next(s), so for all 1 ≤ i ≤ N s we have that fi (s) 6= t,
which implies P0 (s, t) = 0. Otherwise, if P(s, t) > 0, let i be such that f (s, i) = t. Then, we have that
P0 (s, t) = pi (s) = P(s, f (s, i)) = P(s, t).

Remark 5.2. As in Remark 5.1, there are infinite PRBTSs describing a given Markov Chain. As an example,
we could define as many rules as the total number of states, composed by constant functions.

Note that a PRBTS may easily be defined by a program written in a suitable (e.g. C-like) programming lan-
guage. This allows us to specify functions (p, f ) ∈ Rules inside the program as functions or procedures.
This makes their formulation parametric and concise. Indeed, we can state that a rule based (i.e. PRBTS
oriented) approach to SP specification is, in many cases, exponentially shorter than a Markov Chain based
specification approach. By a Markov Chain based specification approach we mean any language requiring,
in many cases, an explicit (i.e. tabular) definition of the stochastic matrix of the input Markov Chain; the
PRISM input language is an example of Markov Chain based approach.

In fact, by comparing the description of protocol LQS in Section 5.4.1 (with FHP-Murϕ, so with a PRBTS)
with the description of the same protocol in PRISM (which is shown, for ITEM Q = 10, in Appendix C),
we can see that the former is much shorter, more concise and more readable than the latter, since it does not
grow with the parameter ITEM Q.

5.3 F ROM C OMMUNICATING S TOCHASTIC P ROCESSES TO PRBTS

To exemplify the PRBTS usage as a low level definition language for SP, in this section we show how the
definition of an SP S specified by means of Communicating Stochastic Processes can be translated into a
suitable PRBTS.

Definition 5.4. A System of Communicating Stochastic Processes (SCSP) S is a 4-tuple (n, S, q, R),
where:

• n is an integer (denoting the number of processes in our system);

• S = S1 × . . . × Sn is the Cartesian product of the finite sets (of states) S1 , . . . , Sn , where Si denotes
the set of states of the i-th process;
1 Indeed, here ti could be any state in next(s).
66 Chapter 5. FHP-Murϕ Language Expressiveness

• q = (q1 , . . . , qn ) ∈ S is the initial state (constituted by the initial state of each Si );


• R = (R1 , . . . , Rn ) is a n-tuple of sets such that, forX
all i = 1, . . . , n, Ri is a finite set of function
pairs (p, f ) where p : S → [0, 1], f : S → Si , and p(s) = 1 holds for all 1 ≤ i ≤ n and for
(p,f )∈Ri
all s ∈ S. Of course, Ri is the collection of rules defining the i-th process.

In the following we denote with boldface letters (e.g. x) elements of S = S 1 × . . . × Sn and with xi the
i-th component of x. We can define the transition relation of a SCSP assuming that processes are scheduled
with uniform probability (1/n if we have n processes).
Definition 5.5. Let S = (n, S, q, R) be a SCSP. The Markov Chain S mc = (S, P, q) associated with S is
n
X X p(s)
defined as follows: P(s, t) = (taking as 0 a summation
i=1
n
(p,f )∈Ri s.t. (s1 ,...,si−1 ,f (s),si+1 ,...,sn )=t
on an empty set).
Proposition 5.4. Let S = (n, S, q, R) be a SCSP. Then, the structure S mc = (S, P, q) associated with S
is indeed a Markov Chain.

Proof. To prove the thesis, we have to show that:

1. for all s, t ∈ S, P(s, t) ≥ 0; this comes from the fact that each P(s, t) is the sum of non-negative
numbers;
X X
2. for all s ∈ S, P(s, t) = 1; in fact, for all s ∈ S we have that P(s, t) =
t∈S t∈S
n
XX X p(s)
=
n
t∈S i=1 (p,f )∈Ri s.t. (s1 ,...,si−1 ,f (s),si+1 ,...,sn )=t
Xn X X n
X X
1 1 n
n p(s) = n p(s) = n =1.
i=1 t∈S (p,f )∈Ri s.t. (s1 ,...,si−1 ,f (s),si+1 ,...,sn )=t i=1 (p,f )∈Ri

Essentially PRBTS are (probabilistic) shared variable concurrent programs. Thus it is not surprising [38]
that a SCSP can be transformed into a PRBTS using a suitable uniform probability scheduler. The following
definition shows how this can be done (e.g. along the lines in PRISM [52]).
Definition 5.6. Let S = (n, S, q, R) be a SCSP. We denote with Γ(S) the PRBTS (S, q, Rules) defined as
follows: Rules = ∪ni=1 ∪(p,f )∈Ri {((λx. p(x)
n ), f )}.

The following proposition follows immediately from the construction in Definition 5.6.
Proposition 5.5. Let S be a SCSP. Then S mc = Γ(S)mc .

Proof. Let S mc = (S, P, q) and Γ(S)mc = (S, P0 , q). Since the set of states and the initial state are
the same in both cases, we only have to prove that P = P0 . To this aim, let s, t ∈ S; then, P0 (s, t) =
n
X X X p(s)
p(s) = = P(s, t).
i=1
n
(p,f )∈Rules s.t.f (s)=t (p,f )∈R i s.t. (s 1 ,...,s i−1 ,f (s),s i+1 ,...,s n )=t
5.4 Two protocols in FHP-Murϕ 67

Note that the PRBTS transformation of a SCSP is not limited to the case in which the processes are sched-
uled with a uniform probability. In fact, it is sufficient to modify Definition 5.6 in this way: Rules
= ∪i=n
i=1 ∪(p,f )∈Ri {(λx.(s(i) · p(x)), f )}, where s is a function from {1, . . . , n} to [0, 1] denoting the
n
X
scheduling probability of the process i ∈ {1, . . . , n} (obviously, s must be such that s(i) = 1).
i=1

5.4 T WO PROTOCOLS IN FHP-M URϕ

In this section we show how FHP-Murϕ can be used for automatic Finite Horizon Verification of PRBTS.

More specifically, we give two examples of our approach describing the behavior of two different queuing
systems, showing their implementation in FHP-Murϕ and sketching why they are more naturally described
in FHP-Murϕ than in PRISM.

Both examples describe queue systems with a certain probability that an element in the queue decides to
leave the queue without having being served. This results in an error state.

5.4.1 A L ENGTH -BASED Q UEUE S YSTEM

The first system models a “Length-Based” Queue System (LQS in the following); its dynamics is described
below. In a generic state s, the following moves are allowed:

1. An enqueue operation. This operation is possible only if the queue is not full;

2. A dequeue operation. This operation is possible only if the queue is not empty;

3. Each element in the queue can depart from the queue (this results in an error state);

4. The system may remain in the same state s.

The probabilities of the preceding moves are as follows. Let n be the number of queue entries. Suppose
that, in state s, h operations are allowed. We have that 1 ≤ h ≤ 3 + n, since each of the at most n
elements in the queue can go in an error state. Then the probability of the first two moves (if they are
−j
allowed) is h1 . The probability that a queue element i enters an error state is 1−eh , where j is the number
of elements preceding i in the queue (i.e. the number of dequeue operations that i must wait for before it is
its turn). This means that the more elements preceding i, the higher the probability that i leaves the queue.
Finally, the probability that no operation is performed is the complement to 1 of the sum of the other defined
probabilities.

The implementation of such a system in FHP-Murϕ is quite simple. The queue is modeled with a circular
array managed by two pointers, head and tail. For each entry in the queue, we memorize if it is in a
correct state or in an error state (i.e. the element has left).

In Figure 5.1 we show the two main functions, prob trans and make trans, and how they are called
by the rule ‘‘main’’.

Function prob trans returns the outgoing probabilities from the current state s. The parameter i identi-
fies the move whose probability has to be computed. Moreover, prob trans uses function prob err to
−j
compute 1−eh , where j is the number of elements preceding an element in the queue. On the other hand,
68 Chapter 5. FHP-Murϕ Language Expressiveness

f u n c t i o n prob_trans(i : trans_allow_type) : real_type;


begin
calc_trans_allow(trans_allow, enq_deq);
/* now, trans allow contains the number of all allowed
moves, while enq deq considers only the enqueue and
dequeue operations */
i f (i >= trans_allow) then
/* not allowed transition (i ranges on all the possible
transitions, but only a subset is allowed) */
r e t u r n 0.0;
e l s e /* allowed transitions */
i f (i < enq_deq) then /* enqueue and dequeue */
r e t u r n 1.0/trans_allow;
e l s e i f (i < trans_allow - 1) then /* leave the queue */
r e t u r n prob_err(i - enq_deq)/trans_allow;
e l s e /* i = trans allow − 1; stay in the same state */
r e t u r n 1.0 - sum_prob_prec();
e n d i f; e n d i f; e n d i f;
end; /* prob_trans() */

procedure make_trans(i : trans_allow_type);


begin
/* the first part is the same as prob trans */
calc_trans_allow(trans_allow, enq_deq);
i f (i < trans_allow) then
/* instead of computing probabilities, moves are done */
i f (!queue_empty() & i = 0) then
/* dequeue is allowed and i indicates dequeue, thus
dequeue operation is performed */
q[head] := noerr;
head := head = ITEM_Q - 1? 0 : head + 1;
e l s e i f (!queue_full() & (enq_deq = 1? i = 0: i = 1)) then
/* the same as above, but with the enqueue operation */
q[tail] := noerr;
tail := tail = ITEM_Q - 1? 0 : tail + 1;
e l s e i f (i != trans_allow - 1) then /* leave the queue */
q[i - enq_deq] := err;
/* if i = trans allow − 1 no action is done */
e n d i f; e n d i f; e n d i f; e n d i f;
end; /* make_trans() */

r u l e s e t i : trans_allow_type do
/* general rule for the whole system */
r u l e "main" prob_trans(i) ==>
b e g i n make_trans(i); end;
end; /* ruleset */

pctl [ t r u e u n t i l <= STEPS


( e x i s t s i : queue_range do q[i] = err e n d e x i s t s )
] <= 0.1;

Figure 5.1: FHP-Murϕ implementation sketch for LQS


5.4 Two protocols in FHP-Murϕ 69

function make trans modifies state s so as to generate a next state. It uses the parameter i in the same
manner as prob trans.

Finally, the ruleset in Figure 5.1 calls the rule ‘‘main’’ with the different values for the variable
i which are needed in functions prob trans and make trans.

For what regards the property to be verified, we want to check if that the probability of the event “for
all states s that are reachable in STEPS steps, s is not an error state” is at most 0.1, where STEPS is a
parameter of the verification.

5.4.2 A T IME -BASED S ERVER -Q UEUE S YSTEM

The second system models a “Time-Based” Server-Queue System (TSQS in the following). Differently
from LQS, we now model not only the queue, but also the server status. Namely, in a generic TSQS state,
there are two different sets of allowed moves. These two sets of moves alternate in execution paths, i.e. if a
state t has been reached by an operation of the first set, then only actions of the second set are allowed from
t, and vice versa. More in detail, these are the two sets of moves:

• The first set just consists of the same enqueue, dequeue and null operations of LQS, plus the server
status changing. These transitions take place with uniform probability; namely, if n of these moves
are allowed, then their probability is n1 . However, the server status changing may be carried out in
different ways. In fact, we model the server status as a counter (s in Figures 5.2 and 5.3) ranging
from 0 to MAX COUNT S, representing the time of service. If the server counter is 0, the server is
free, then a dequeue (on a nonempty queue) can be made. In this case, the server counter is set
to MAX COUNT S; as already said, the probability of this transition is n1 . Otherwise, if the server
counter is greater than 0, then it can be reset to 0 or simply decremented. The probabilities of these
transitions are, respectively, n1 · MAX COUNT
s
 S (i.e. the probability is proportional to the current server
1 s
counter value) and n · 1 − MAX COUNT S . This models the fact that the higher the time of service, the
higher the probability of returning free. Note that, the sum of these two probabilities is n1 , i.e. the
overall probability for the server status change.
• The second set consists of moves updating a counter associated with each element in the queue, thus
modeling the time spent by the element in the queue. When this counter reaches a given maximum
value (MAX COUNT Q), we are in an error state. The updating phase consists in k + 1 possible
transitions, where k is the number of elements currently in the queue. Namely, one of the k element
counters can immediately reach MAX COUNT Q with probability directly proportional to the current
counter value, while all the other counters are simply incremented. Moreover, the last possibility
consists in incrementing all counters; the probability of this move is the complement to one of all the
previous ones. This models the fact that the higher the time spent in queue, the higher the probability
to go away without being served.

The FHP-Murϕ implementation of TSQS is as simple as the LQS one, and it is sketched in Figures 5.2 and
5.3. The data structures are essentially the same as in LQS: the only modification consists in maintaining a
counter (and not a boolean) for each entry, and in adding a counter to model the server. The structure of the
code is the same as in Figure 5.1, so we only give functions prob trans and make trans.

For what regards the property to be verified, we want to check, analogously as for LQS, if that the probability
of the event “for all states s that are reachable in STEPS of steps, s is not an error state” is at most 0.1,
where STEPS is a parameter of the verification.

Note that both these TSQS and LQS are more difficult to write in the PRISM input language. In fact,
PRISM only allows constant probabilities to be defined on transitions. On the other hand, here we have that
70 Chapter 5. FHP-Murϕ Language Expressiveness

f u n c t i o n prob_trans(i : trans_allow) : real_type;


begin
calc_trans_allow(trans_allow);
/* now, trans allow contains the number of allowed
transitions from the current state. trans allow is
different from n of Section 5.4.2, since the two server
status changing modalities are both counted */
i f (i >= trans_allow) then
/* not allowed transition (i ranges on all the possible
transitions, but only a subset is allowed) */
r e t u r n 0.0;
else
/* modg lob distinguish between the two set of moves */
i f (mod_glob = 0) then
/* first set of moves */
i f (s > 0 & i < 2) then
/* probabilities for server status changing */
/* the server counter is modifiable only if it is
greater than 0 (thus, the server is busy) */
i f (i = 1) then
/* server counter to be decremented */
r e t u r n (s/MAX_COUNT_S)/(trans_allow - 1);
/* we divide by (trans allow − 1) because the first two
transitions are already complementary */
else
/* i = 0: server counter to be reset */
r e t u r n (1.0 - s/MAX_COUNT_S)/(trans_allow - 1);
e n d i f;
else
/* probabilities for enqueue, dequeue and no operation */
/* n1 is returned; we only have to take care of the fact
that trans allow = n − 1 when s > 0, since both the two
server status changing modalities are counted */
r e t u r n 1.0/(s > 0? trans_allow - 1 : trans_allow);
endif;
e l s e /* mod glob = 1 */
/* second set of moves */
i f (i != trans_allow - 1) then
/* probability of one among the k moves of Section 5.4.2
(go to error) */
/* entry(i) returns the i-th element in the queue */
r e t u r n (q[entry(i)]/MAX_COUNT)/trans_allow;
else
/* probability of k + 1-th move of Section 5.4.2 (all
counters incremented) */
r e t u r n 1.0 - sum_prob_prec();
e n d i f; e n d i f;
endif;
end; /* prob_trans() */

Figure 5.2: FHP-Murϕ implementation sketch for TSQS (1)


5.4 Two protocols in FHP-Murϕ 71

/* auxiliary function, returns true if i indicates an


enqueue */
f u n c t i o n it_is_time_to_enqueue(i : trans_allow) : b o o l e a n;
begin
i f (queue_full()) then /* enqueue not allowed */
return f a l s e ;
e l s e /* enqueue allowed, check i */
i f (s > 0) then r e t u r n (i > 3);
else
i f (queue_empty()) then r e t u r n (i = 0);
e l s e r e t u r n (i < 2);
e n d i f; e n d i f; e n d i f;
end; /* it_is_time_to_enqueue() */

procedure make_trans(i : trans_allow);


begin
calc_trans_allow(trans_allow);
i f (i < num_trans_allow) then
i f (mod_glob = 0) then
i f (s > 0 & i < 2) then s := (i = 1? s - 1 : 0);
else
i f (!queue_empty() & s = 0 & i = 0) then
dequeue(); /* no matter what is dequeued */
s := MAX_COUNT_S;
else
i f (it_is_time_to_enqueue(i)) then
enqueue(); /* no matter what is enqueued */
e n d i f; e n d i f; e n d i f;
e l s e /* mod glob = 1 */
i f (i != num_trans_allow - 1) then
/* entry(i) returns the i-th element in the queue */
q[entry(i)] := MAX_COUNT;
endif;
f o r k : queue_range do
i f (in_queue(k) & q[k] != MAX_COUNT) then
q[k] := q[k] + 1;
endif;
endfor;
endif;
e n d i f ; /* if i = trans allow − 1 no action is done */
/* switch to the other set of moves */
mod_glob := (mod_glob + 1) mod 2;
end; /* make_trans() */

pctl [ t r u e u n t i l <= STEPS


( e x i s t s i:queue_range do q[i] = MAX_COUNT e n d e x i s t s)
] <= 0.1;

Figure 5.3: FHP-Murϕ implementation sketch for TSQS (2)


72 Chapter 5. FHP-Murϕ Language Expressiveness

the transition probabilities depends on the current state. Hence, to implement these protocols in PRISM,
we are forced to list the values of the parameters from which they depend, and then to tabulate, for each of
them, the transition probability values.

As an example, consider again LQS: to describe it with the PRISM input language, we have to list all the
possible values representing the number of elements preceding the current one, asking for each of them if it
is the correct value; the result is shown in Figure 5.4 for a server with 4 entries. Note that the PRISM code
in Figure 5.4 is not parametric, i.e. it is not sufficient to modify the ITEM Q parameter to obtain the model
for the LQS with more than 4 entries; on the opposite, this is possible in FHP-Murϕ. In Appendix C we
show the PRISM source code for LQS with ITEM Q = 10.
5.4 Two protocols in FHP-Murϕ 73

probabilistic
c o n s t ITEM_Q = 4;
−1
r a t e exp_f_1_d_4 = 0.15803013970713941960;// 1−e4
−2
r a t e exp_f_2_d_4 = 0.21616617919084682702;// 1−e4
r a t e a_third = 0.33333333333333333333;
r a t e a_fourth = 0.25;
−1
r a t e rem_1 = 0.34196986029286058040;//1 − 21 − 1−e4
−1 −2
r a t e rem_2 = 0.37580368110201375338;//1 − 41 − 1−e4 − 1−e4
module main
q0 : [0..1];// 0 : noerr; 1 : err
q1 : [0..1];// 0 : noerr; 1 : err
q2 : [0..1];// 0 : noerr; 1 : err
q3 : [0..1];// 0 : noerr; 1 : err
num : [0..ITEM_Q - 1];// number of items in the queue
h : [0..ITEM_Q - 1];// head of the queue
// 2 moves (enqueue and nomove)
[] (num = 0) -> 0.5 : (num’ = num+1) + 0.5 : (num’ = num);
// 3 moves (enqueue, dequeue, nomove)
[](num=1&h=ITEM_Q-1)->a_third:(num’=num+1)+
a_third:(num’=num-1&h’=0)+
a_third:(num’=num);
[](num=1&h!=ITEM_Q-1)->a_third:(num’=num+1)+
a_third:(num’=num-1&h’=h+1)+
a_third:(num’=num);
// 4 moves (enqueue, dequeue, 1 item status change, nomove)
[](num=2&h=0)->a_fourth:(num’=num+1)+
a_fourth:(num’=num-1&h’=1)+
exp_f_1_d_4:(q1’=1)+rem_1:(num’=num);
[](num=2&h=1)->a_fourth:(num’=num+1)+
a_fourth:(num’=num-1&h’=2)+
exp_f_1_d_4:(q2’=1)+rem_1:(num’=num);
[](num=2&h=2)->a_fourth:(num’=num+1)+
a_fourth:(num’=num-1&h’=3)+
exp_f_1_d_4:(q3’=1)+rem_1:(num’=num);
[](num=2&h=3)->a_fourth:(num’=num+1)+
a_fourth:(num’=num-1&h’=0)+
exp_f_1_d_4:(q0’=1)+rem_1:(num’=num);
// 4 moves (dequeue, 2 items status change, nomove)
[](num=3&h=0)->a_fourth:(num’=num-1&h’=1)+
exp_f_1_d_4:(q1’=1)+
exp_f_2_d_4:(q2’=1)+rem_2:(num’=num);
[](num=3&h=1)->a_fourth:(num’=num-1&h’=2)+
exp_f_1_d_4:(q2’=1)+
exp_f_2_d_4:(q3’=1)+rem_2:(num’=num);
[](num=3&h=2)->a_fourth:(num’=num-1&h’=3)+
exp_f_1_d_4:(q3’=1)+
exp_f_2_d_4:(q0’=1)+rem_2:(num’=num);
[](num=3&h=3)->a_fourth:(num’=num-1&h’=0)+
exp_f_1_d_4:(q0’=1)+
exp_f_2_d_4:(q1’=1)+rem_2:(num’=num);
endmodule

Figure 5.4: PRISM implementation for LQS with 4 queue entries


C HAPTER 6

C ONCLUSIONS

In this Thesis, we have showed that it is possible, and most of all effective, to design algorithms of explicit
type for Probabilistic Model Checking.

To this end, we presented two algorithms able to verify if a given a Markov Chain satisfies a given finite
horizon property. The first algorithm only deals with finite horizon safety properties, i.e. it may check
whether the probability of reaching an error state in at most k steps is below a given threshold α. On the
other hand, the second algorithm handles the full BPCTL (Bounded PCTL), so allowing to verify more
complex properties, such as e.g. robustness in Discrete Time Probabilistic Hybrid Systems. Thus, both our
algorithm work on bounded properties, i.e. they consider only system runs of finite length.

In order to show the effectiveness of our approach, we implemented our algorithms within a suitable ex-
tension of the Murϕ verifier. We call FHP-Murϕ (Finite Horizon Probabilistic Murϕ) such extension of
the Murϕ verifier. This allows us to make experiments, by comparing FHP-Murϕ with PRISM, a state-
of-the-art symbolic model checker for Markov Chains. Our experimental results show that FHP-Murϕ can
effectively handle verifications for systems that are out of reach for PRISM, namely those involving arith-
metic operations on the state variables. Moreover, we also showed how FHP-Murϕ have been successfully
used to verify interesting stochastic properties on a “real world” discrete time probabilistic hybrid system,
namely the Control System of ICARO, a system in operation at the ENEA Research Center of Casaccia
(Italy).

In conclusion, we have showed that the explicit approach to probabilistic model checking is feasible and
effective, and that FHP-Murϕ has to be considered among the available probabilistic model checkers.

6.1 F UTURE W ORKS

A promising future works direction is to extend FHP-Murϕ, in order to make it more complete w.r.t.
PRISM. In fact, PRISM can actually handle, besides Discrete Time Markov Chains, also Markov Decision
Processes (MDP) and Continuous Time Markov Chains (CTMC). Moreover, on Discrete Time Markov
Chains PRISM is able to verify any PCTL formula, including infinite horizon properties, while FHP-Murϕ
only deals with bounded properties. However, PRISM always uses symbolic algorithms, so its approach
works well as long as the system dynamics does not involve heavy arithmetical computations. Thus, ex-
tending our approach will probably enlarge the class of automatically verifiable probabilistic systems.

In particular, let us consider CTMCs, which is a widely used model for stochastic systems [8, 9, 29, 32, 36].
In order to make FHP-Murϕ able to verify a finite horizon CSL properties Φ on a CTMC C, we may
approximate C with a proper Discrete Time Markov Chain MC . Then, our algorithms may be applied to
verify if Φ |= MC , and, finally, a performance comparison with PRISM may be carried out.

75
A PPENDIX A

FHP-M URϕ: THE I NPUT L ANGUAGE

Like the other explicit model checkers, a Murϕ verification is made of three commands:

1. the Murϕ compiler mu (which results from the make compilation of the files in the src directory in
the Murϕ distribution) is invoked on a file NFSS.m describing the NFSS to be analyzed. This gener-
ates a file NFSS.C, containing the C++ code implementing the body of rules, start states, invariants,
functions and procedures, plus other stuffs. Of course, it is possible to pass various options to mu,
e.g. to enable hash compaction (-c).
2. the file NFSS.C generated in step 1 is compiled with the standard C++ compiler, setting as including
directory the include directory of the Murϕ distribution. This allows to use the main verification
routines implemented in the files in the include directory. We call NFSS the compilation result.
3. to begin the verification, it is sufficient to launch the NFSS executable created in step 2. As in step 1,
it is possible to pass various options to the verifier, e.g. to set the maximum RAM memory available
(e.g. -mN , being N the available amount of RAM memory in megabytes).

To implement FHP-Murϕ, starting from the original Murϕ code, we modify both the files in the src
directory and in the include directory. In this appendix, we will show in detail the modifications done to
files in the src directory, while in Appendix B we focus on the include directory

Thus, in this appendix, we give the technical details on how we modify the Murϕ input language in order
to make it able to define a Markov Chain. In Section A.1 we describe the modifications we have done to the
Murϕ input language grammar, while in Section A.2 we show the Lex, Yacc and C++ code implementing
such modifications, which is stored in the files in src directory.

A.1 A N OVERALL D ESCRIPTION

To make the original Murϕ input language able to define Markov Chains, we modify it in the following
parts:

1. We add a probability specification to each start state;


2. We change the semantics of rules;
3. We allow to specify BPCTL properties (see Section 2.2).

To allow the specification of Discrete Time Stochastic Hybrid Systems, it is useful to have state variables
ranging on real numbers. For this reason in the following we will consider the Murϕ version enhanced with
finite precision real numbers, as described in [17].

77
78 Chapter A. FHP-Murϕ: the Input Language

In the following, to describe the new language, we give the BNF of the nonterminals we modified; as usual,
we enclose with brackets [] the optional parts and with braces {} the optional parts which can be repeated
more than one time. When brackets or braces are indeed part of the language, they will be preceded by a
backslash \[\], \{\}. Moreover, nonterminals will be enclosed by angle brackets <>.

To add probabilities on rules, we modify the semantics of the simplerule nonterminal production of
the Murϕ language grammar (Chapter 7 of the documentation [47]) as follows. The original production,
without priority and fairness (not modified in our work), was

<simplerule> ::= rule [ <expr> ] ==> [ {<decl>} begin ] [ <stmts> ] end.

In FHP-Murϕ, we simply require the expression after the keyword rule (i.e. expr) to be a real expression
valued in [0, 1], instead of a boolean as it is for Murϕ. FHP-Murϕ does not allow simultaneous use of both
boolean and probability based rules.

The above modification to <simplerule> has a deep impact on Murϕ semantics. In fact, with boolean
rules, each state has a set of enabled transitions, leading to other states; the activation of a rule only depends
on its condition being true or false. In our probabilistic setting, each Murϕ rule defines a pair (p, f ) of the
PRBTS being defined.

Finally, we modify the main <prog> nonterminal, to force each FHP-Murϕ model to end with exactly one
BPCTL properties. Thus, we introduce a new nonterminal <bpctl>, defining the BPCTL language:

<bpctl> ::= pctl <bpctlformula>


<bpctl f> ::= <bpctl f> && <bpctl f>
| <bpctl f> || <bpctl f>
| !!<bpctl f>
| (<bpctl f>)
| <expr>
| \[ <bpctl f> UNTIL <= <expr> <bpctl f> \] w <expr>
| \[ NEXT <bpctl f> \] w <expr>

A.2 C++, L EX AND YACC C ODE

We now present the modifications we did to the original Murϕ to implement our new input language. To do
so, it was necessary to modify the code of some files in the src directory of the original Murϕ distribution.
Here, we present only the modified parts, giving also the context in which they were done.

Finally, from a user point of view, the Murϕ compiler mu has been modified so as to include three new
options:

1. --bpctl is the more important one, since it generates the C++ file for the probabilistic verification,
starting from a PFSS model.

2. --probequalk is similar to the preceding one, but starts from a NFSS model. This is translated in
a PFSS models by simply assuming that, if the current state has n outgoing edges, then each of the
transitions will have probability n1 .

3. --probrules is the opposite of the preceding one: it takes a PFSS model and translates it in a
NFSS (a rule is activated if its probability is strictly greater than zero).
A.2 C++, Lex and Yacc Code 79

A.2.1 M ODIFICATIONS TO MU . Y
%{

..
.
v o i d pctlUntilError( b o o l is_real, b o o l is_int, b o o l has_value, double
value, b o o l is_int_buntil, b o o l has_value_buntil, i n t buntil)
{
Error.CondError(!is_real && !is_int, "Probability bound for pctl
formulas must be real.");
Error.CondError(!has_value, "Probability bound for pctl formulas must
be constant.");
Error.CondError(value < 0.0 || value > 1.0, "Probability bound for
pctl formulas must be in [0, 1].");
Error.CondError(!is_int_buntil, "Bound for UNTIL must be integer.");
i f (!Error.CondError(!has_value_buntil, "Bound for UNTIL must be
constant."))
Error.CondError(buntil < 0, "Bound for UNTIL must be at least 0.");
}
v o i d pctlNextError( b o o l is_real, b o o l is_int, b o o l has_value, double
value)
{
Error.CondError(!is_real && !is_int, "Probability bound for pctl
formulas must be real.");
Error.CondError(!has_value, "Probability bound for pctl formulas must
be constant.");
Error.CondError(value < 0.0 || value > 1.0, "Probability bound for
pctl formulas must be in [0, 1].");
}
%}

..
.
%token<real> REALCONST
%token REAL
%token LOG
%token LOG10
%token EXP
%token SIN
%token COS
%token TAN
%token FABS
%token FLOOR
%token CEIL
%token SQRT
%token FMOD
%token POW
%token ASIN
%token ACOS
%token ATAN
%token SINH
%token COSH
%token TANH

..
.
%left PCTLOR
%left PCTLAND
%left PCTLNOT
80 Chapter A. FHP-Murϕ: the Input Language

..
.
%type <typedecl_p> realtype

..
.
%type <pctl_p> pctl pctlformula pctlU pctlX

..
.
/* Starting nonterminal */
prog :
/* this is as before */
{ symtab->pushscope(); }
decls
{ theprog->globals = symtab->getscope(); }
procDecls
{ theprog->procedures = symtab->getscope(); }
rules
{
theprog->bits_in_world = offset;
theprog->rules = $6;
Error.CondError(!hasrule, "Program has no rules.");
Error.CondError(!has_startstate,
"Program has no startstates.");
}
/* this is added */
pctl
{
theprog->pctl_form = $8;
symtab->popscope();
Error.CondError(symtab->topscope() != 0, "Internal: Didn’t \
pop enough scopes.\nIf the model seems to be correct, you may have\
improperly used a reserved word");
};

..
.
pctl : PCTL pctlformula semi
{
Error.CondError(!args->bpctl && !args->pmurphik,
"PCTL formulas supported only with --bpctl or --pmurphik.");
$$ = $2;
}
| /* empty */
{
Error.CondError(args->bpctl || args->pmurphik,
"PCTL formulas required with --bpctl and --pmurphik.");
$$ = NULL;
};
pctlformula : pctlformula PCTLAND pctlformula
{
$$ = new pctl($1, $3, NULL, AND_PCTL, 0, NULL);
}
| pctlformula PCTLOR pctlformula
{
$$ = new pctl($1, $3, NULL, OR_PCTL, 0, NULL);
A.2 C++, Lex and Yacc Code 81

}
| pctlformula PCTLIMPLIES pctlformula
{
$$ = new pctl($1, $3, NULL, IMPL_PCTL, 0, NULL);
}
| PCTLNOT pctlformula
{
$$ = new pctl($2, NULL, NULL, NOT_PCTL, 0, NULL);
}
| ’(’ pctlformula ’)’
{
$$ = $2;
}
| pctlU
| pctlX
{
$$ = $1;
}
| expr //atomic proposition
{
Error.CondError(!type_equal($1->gettype(), booltype),
"Atomic propositions in BPCTL formulas have to be \
boolean expressions.");
$$ = new pctl(NULL, NULL, $1, AP_PCTL, 0, NULL);
};
pctlU : ’[’ pctlformula UNTIL LEQ expr pctlformula ’]’ ’<’ expr
{
pctlUntilError(type_equal($9->gettype(), realtype),
type_equal($9->gettype(), inttype), $9->hasvalue(),
(double )$9->getvalue(), type_equal($5->gettype(), inttype),
$5->hasvalue(), $5->getvalue());
$$ = new pctl($2, $6, NULL, UNTIL_PCTL, $5->getvalue(), $9);
$$->op = PCTL_L;
}
| ’[’ pctlformula UNTIL LEQ expr pctlformula ’]’ LEQ expr
{
pctlUntilError(type_equal($9->gettype(), realtype),
type_equal($9->gettype(), inttype), $9->hasvalue(),
(double )$9->getvalue(), type_equal($5->gettype(), inttype),
$5->hasvalue(), $5->getvalue());
$$ = new pctl($2, $6, NULL, UNTIL_PCTL, $5->getvalue(), $9);
$$->op = PCTL_LEQ;
}
| ’[’ pctlformula UNTIL LEQ expr pctlformula ’]’ ’>’ expr
{
pctlUntilError(type_equal($9->gettype(), realtype),
type_equal($9->gettype(), inttype), $9->hasvalue(),
(double )$9->getvalue(), type_equal($5->gettype(), inttype),
$5->hasvalue(), $5->getvalue());
$$ = new pctl($2, $6, NULL, UNTIL_PCTL, $5->getvalue(), $9);
$$->op = PCTL_G;
}
| ’[’ pctlformula UNTIL LEQ expr pctlformula ’]’ GEQ expr
{
pctlUntilError(type_equal($9->gettype(), realtype),
type_equal($9->gettype(), inttype), $9->hasvalue(),
(double )$9->getvalue(), type_equal($5->gettype(), inttype),
$5->hasvalue(), $5->getvalue());
$$ = new pctl($2, $6, NULL, UNTIL_PCTL, $5->getvalue(), $9);
$$->op = PCTL_GEQ;
};
pctlX : ’[’ NEXT pctlformula ’]’ ’<’ expr
82 Chapter A. FHP-Murϕ: the Input Language

{
pctlNextError(type_equal($6->gettype(), realtype),
type_equal($6->gettype(), inttype), $6->hasvalue(),
( double )$6->getvalue());
$$ = new pctl($3, NULL, NULL, NEXT_PCTL, 0, $6);
$$->op = PCTL_L;
}
| ’[’ NEXT pctlformula ’]’ LEQ expr
{
pctlNextError(type_equal($6->gettype(), realtype),
type_equal($6->gettype(), inttype), $6->hasvalue(),
( double )$6->getvalue());
$$ = new pctl($3, NULL, NULL, NEXT_PCTL, 0, $6);
$$->op = PCTL_LEQ;
}
| ’[’ NEXT pctlformula ’]’ ’>’ expr
{
pctlNextError(type_equal($6->gettype(), realtype),
type_equal($6->gettype(), inttype), $6->hasvalue(),
( double )$6->getvalue());
$$ = new pctl($3, NULL, NULL, NEXT_PCTL, 0, $6);
$$->op = PCTL_G;
}
| ’[’ NEXT pctlformula ’]’ GEQ expr
{
pctlNextError(type_equal($6->gettype(), realtype),
type_equal($6->gettype(), inttype), $6->hasvalue(),
( double )$6->getvalue());
$$ = new pctl($3, NULL, NULL, NEXT_PCTL, 0, $6);
$$->op = PCTL_GEQ;
};
/* general expression. */
expr :

..
.

| REALCONST
{
$$ = new expr( $1, realtype);
}
| LOG ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,1);
}
| LOG10 ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,2);
}
| EXP ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,3);
}
| SIN ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,4);
}
| COS ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,5);
}
| TAN ’(’ expr ’)’
A.2 C++, Lex and Yacc Code 83

{
$$ = new mathexpr($3,NULL,6);
}
| FABS ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,7);
}
| FLOOR ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,8);
}
| CEIL ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,9);
}
| SQRT ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,10);
}
| FMOD ’(’ expr ’,’ expr ’)’
{
$$ = new mathexpr($3,$5,11);
}
| POW ’(’ expr ’,’ expr ’)’
{
$$ = new mathexpr($3,$5,12);
}
| ASIN ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,13);
}
| ACOS ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,14);
}
| ATAN ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,15);
}
| SINH ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,16);
}
| COSH ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,17);
}
| TANH ’(’ expr ’)’
{
$$ = new mathexpr($3,NULL,18);
}

..
.

A.2.2 M ODIFICATIONS TO MU . L

..
.
REAL ({DIGIT}+"."{DIGIT}+)
"!!" { r e t u r n( PCTLNOT ); }
"->" { r e t u r n( IMPLIES ); }
84 Chapter A. FHP-Murϕ: the Input Language

"->>" { r e t u r n( PCTLIMPLIES ); }
"&&" { r e t u r n( PCTLAND ); }
"||" { r e t u r n( PCTLOR ); }
{REAL} {
sscanf( yytext, "%le", &(yylval.real) );
r e t u r n(REALCONST);
}

..
.

A.2.3 M ODIFICATIONS TO LEXTABLE .C

..
.
lextable::lextable()
{

..
.
/* IM: PCTL (until already present) */
enter_reserved("pctl_next", NEXT);
enter_reserved("pctl", PCTL);
enter_reserved("real",REAL);
enter_reserved("log",LOG);
enter_reserved("log10",LOG10);
enter_reserved("exp",EXP);
enter_reserved("sin",SIN);
enter_reserved("cos",COS);
enter_reserved("tan",TAN);
enter_reserved("fabs",FABS);
enter_reserved("floor",FLOOR);
enter_reserved("ceil",CEIL);
enter_reserved("sqrt",SQRT);
enter_reserved("fmod",FMOD);
enter_reserved("pow",POW);
enter_reserved("asin",ASIN);
enter_reserved("acos",ACOS);
enter_reserved("atan",ATAN);
enter_reserved("sinh",SINH);
enter_reserved("cosh",COSH);
enter_reserved("tanh",TANH);
}

A.2.4 M ODIFICATIONS TO MU . H

..
.
// pctl extension
enum pctl_type {UNTIL_PCTL, NEXT_PCTL, AP_PCTL, AND_PCTL, OR_PCTL,
IMPL_PCTL, NOT_PCTL};
enum pctl_ord {PCTL_L, PCTL_LEQ, PCTL_G, PCTL_GEQ};

..
.
A.2 C++, Lex and Yacc Code 85

/********************
class argclass
-- command line argument handling
-- a registry of command-line options.
-- Argclass inspired by Andreas\’ code.
********************/
c l a s s argclass
{
i n t argc;
char **argv;
public:
// variables
/* name of the input file */
char *filename;
/* whether to print license */
b o o l print_license;
/* whether to print option */
b o o l help;
/* whether not to bitpack the state vector */
b o o l no_compression;
/* whether not to hashcompact states in hash tbl */
b o o l hash_compression;
/* IM: cached murphi */
b o o l hash_cache;
/* IM: disk murphi */
b o o l hash_disk;
/* IM: standard verification starting from probabilistic rules */
b o o l prob_rules;
/* IM: probabilistic murphi with finite horizon; now used only as
internal variable */
b o o l pmurphik;
/* IM: probabilistic murphi with finite horizon, with boolean
expressions on rules */
b o o l probequalk;
/* IM: bounded probabilistic model checking */
b o o l bpctl;
/* runtime checking? */
c o n s t b o o l checking;
/* suppress code generation for dead rules */
b o o l remove_deadrule;
/* symmetry algorithm number */
i n t symmetry_algorithm_number;
// initializer
argclass( i n t ac, char** av);
// supporting routines
char *NextFile();
b o o l Flag(char *arg);
v o i d PrintInfo( v o i d );
v o i d PrintOptions( v o i d );
v o i d PrintLicense( v o i d );
};

..
.
/********************
Class program
-- for storing parsed tree
********************/
s t r u c t program :TNode
{
i n t bits_in_world;
ste *globals;
86 Chapter A. FHP-Murϕ: the Input Language

ste *procedures;
rule *rules;
pctl *pctl_form; // IM: pctl formula
rulerec *rulelist;
rulerec *startstatelist;
rulerec *invariantlist;
rulerec *fairnesslist;
liverec *livenesslist;
symmetryclass symmetry;
program( v o i d )
: globals(NULL), procedures(NULL),
rules(NULL), rulelist(NULL), startstatelist(NULL),
invariantlist(NULL), fairnesslist(NULL), livenesslist(NULL),
pctl_form(NULL)
{}
v o i d make_state( i n t bits_in_world);
v i r t u a l char *generate_code();
};

..
.
/********************
Yacc’s YYSTYPE
-- Yacc’s YYSTYPE, done here instead of in a %union in yacc
-- so that we don’t get redefinitions when y.tab.c #include’s
-- this which #include’s y_tab.h.
********************/
union YYSTYPE { /* sloppy, sloppy, sloppy. Bleah. */
TNode *node;
expr *expr_p; // an expression.
designator *desig_p; // a designator (l-value).
decl *decl_p; // a declaration.
typedecl *typedecl_p; // a type declaration.
ste *ste_p; // a record field list or formal parameter list
stmt *stmt_p; // a statement or sequence of statements.
rule *rule_p; // a sequence of rules.
pctl *pctl_p; // a sequence of rules.
ifstmt *elsifs; // a sequence of elsif clauses.
caselist *cases; // a sequence of cases of a switch statement.
alias *aliases; // a sequence of alias bindings.
i n t integer; // an integer constant.
double real; // a real constant
b o o l boolean; // a boolean value--used with INTERLEAVED.
lexid *lex; // a lexeme, most usefully an ID.
lexlist *lexlist_p; // a list of lexemes.
char *string; // a string value.
exprlist *exprlist_p; // a list of expressions.
};

A.2.5 M ODIFICATIONS TO MU .C

..
.
argclass::argclass( i n t ac, char** av)
: argc(ac), argv(av), print_license(FALSE), help(FALSE),
checking(TRUE), no_compression(TRUE), hash_compression(FALSE),
hash_disk(FALSE), hash_cache(FALSE), pmurphik(FALSE),
probequalk(FALSE), bpctl(FALSE)
{
b o o l initialized_filename = FALSE;
A.2 C++, Lex and Yacc Code 87

i n t i;
i f (ac==1) { // if there is no argument, print help
help = TRUE;
PrintInfo();
exit(1);
}
f o r (i = 1; i < ac; i++) {

..
.

//im: finite horizon probabilistic murphi with boolean rules


i f (strcmp(av[i], "--probrules") == 0) {
i f (hash_cache == TRUE) {
fprintf(stderr, "Option --probrules cannot be used with --cac\
he\.\n");
exit(1);
}
i f (hash_disk == TRUE) {
fprintf(stderr, "Option --probrules cannot be used with --dis\
k.\n");
exit(1);
}
i f (prob_rules == TRUE) {
fprintf(stderr, "Option --probrules cannot be used with --pro\
b.\n");
exit(1);
}
probequalk = TRUE;
continue ;
}
//im: normal verification algorithm, but probabilistic rules
i f (strcmp(av[i], "--prob") == 0) {
i f (bpctl == TRUE) {
fprintf(stderr, "Option --prob cannot be used with --bpctl.\n\
");
exit(1);
}
i f (probequalk == TRUE) {
fprintf(stderr, "Option --prob cannot be used with --probrule\
s.\n");
exit(1);
}
prob_rules = TRUE;
continue ;
}
//im: bounded probabilistic model checking
i f (strcmp(av[i], "--bpctl") == 0) {
i f (hash_cache == TRUE) {
fprintf(stderr, "Option --bpctl cannot be used with --cache.\\
n");
exit(1);
}
i f (hash_disk == TRUE) {
fprintf(stderr, "Option --bpctl cannot be used with --disk.\n\
");
exit(1);
}
i f (probequalk == TRUE) {
fprintf(stderr, "Option --bpctl is actually not usable in con\
junction with --probequalk.\n");
exit(1);
}
88 Chapter A. FHP-Murϕ: the Input Language

i f (prob_rules == TRUE) {
fprintf(stderr, "Option --bpctl cannot be used with --prob.\n\
");
exit(1);
}
bpctl = TRUE;
continue ;
}

..
.
} /* end for */
i f (probequalk) {
i f (hash_compression) {
fprintf(stderr, "\nWarning: option --probrules automatically di\
sables -c.\n\n");
hash_compression = FALSE;
}
pmurphik = TRUE;
}
i f (bpctl && hash_compression) {
fprintf(stderr, "\nWarning: option --bpctl automatically disable\
s -c.\n\n");
hash_compression = FALSE;
}
i f (bpctl || prob_rules)
pmurphik = TRUE;

..
.
v o i d argclass::PrintOptions( v o i d )
{
printf("The options are shown as follows:\n\
\n\
\t-h \t\t \thelp\n\
\t-l \t\t \tprint license\n\
\t-b \t\t \tuse bit-compacted states\n\
\t-c \t\t \tuse hash compaction\n\
\t--cache \t\t \tuse simple state caching\n\
\t--disk \t\t \tuse disk based caching\n\
\t--probrules \t\t \tuse probabilistic murphi with boolean condi\
tions\n\
\t--prob \t\t \tuse standard verification starting from pro\
babilistic rules\n\
\t--bpctl \t\t \tbounded probabilistic model checking\n\
\n\
An argument without a leading ’-’ is taken to be the input filenam\
e,\n\
\twhich must end with ’.m’\n\
");
}

A.2.6 M ODIFICATIONS TO RULE . H

..
.
A.2 C++, Lex and Yacc Code 89

/********************
class pctl
********************/
s t r u c t pctl : p u b l i c TNode
{
expr *atomic_proposition;
pctl_type pctltype;
pctl *subformula1, *subformula2;
pctl_ord op; /* comparison operator */
double prob_bound;
u n s i g n e d until_bound;
/* code of the subformula in the PCTL formula synctactic tree */
i n t code;
pctl(pctl *subf1, pctl *subf2, expr *ap, pctl_type pctltype,
i n t until_bound, expr *prob_bound);
v i r t u a l char *generate_code();
v i r t u a l v o i d generate_atomic_subformulas(pctl *);
i n l i n e c o n s t char *getstring(pctl_type);
i n l i n e c o n s t char *getstring(pctl_ord);
v i r t u a l v o i d generate_non_atomic_subformulas(pctl *);
};

A.2.7 M ODIFICATIONS TO RULE .C

..
.
simplerule::simplerule(ste *enclosures, expr *condition,
ste *locals, stmt *body, b o o l unfair, i n t priority)
: rule(), name(name), enclosures(enclosures), condition(condition),
locals(locals), body(body), condname(NULL), rulename(NULL),
unfair(unfair), priority(priority)
{
/* this was added */
i f (condition != NULL && body != NULL)
{
Error.CondError(condition->has_side_effects(),
"Rule Condition must not have side effects.");
i f (args->bpctl && !args->probequalk)
{
Error.CondError(!type_equal(condition->gettype(),realtype),
"Condition for probabilistic rule must be a real expression.");
i f (condition->hasvalue() &&
((condition->getrvalue()<0) || (condition->getrvalue()>1)))
Error.Error("Real condition for rule must be >=0 and <=1.");
}
else
Error.CondError(!type_equal(condition->gettype(),booltype),
"Condition for rule must be a boolean expression.");
}
/* the rest of the function remains unchanged */
NextSimpleRule = SimpleRuleList;
SimpleRuleList = t h i s ;
size = CountSize(enclosures);
// Rearrange enclosures so that the ones that are NOT
// mentioned in the condition go first
rearrange_enclosures();
}

..
.
90 Chapter A. FHP-Murϕ: the Input Language

/*********************
class pctl
*********************/
pctl::pctl(pctl *subf1, pctl *subf2, expr *ap, pctl_type pctltype,
i n t until_bound, expr *prob_bound)
: subformula1(subf1), subformula2(subf2), atomic_proposition(ap),
pctltype(pctltype), code(new_int()), until_bound(until_bound)
{
Error.CondError(pctltype == AP_PCTL && ap->has_side_effects(),
"Atomic propositions in PCTL formulas mustn’t have side effects.");
i f (prob_bound != NULL)
/* this is a X- or a X-formula */
t h i s ->prob_bound = prob_bound->getrvalue();
}

A.2.8 M ODIFICATIONS TO DECL . H

..
.
c l a s s typedecl: p u b l i c decl
{

..
.
enum typeclass {Real, Enum, Range, Array, MultiSet, MultiSetID,
Record, Scalarset, Union, Error_type};

..
.
}

..
.
e x t e r n typedecl * realtype;
/* AP: declaration of realtypedecl (real type variable’s declaration)
*/
c l a s s realtypedecl: p u b l i c typedecl
{
// accuracy is the number of decimal digit used to represent a real
// exponent is the number of digit used for the exponent
i n t accuracy,exponent;
// maximum positive exponent value (negative is -exponent_value+1)
i n t exponent_value;
public:
// initializer
realtypedecl( i n t ac, i n t ex);
realtypedecl(expr *accuracy,expr *exponent);
// supporting routines
v i r t u a l typedecl *gettype( v o i d ) c o n s t { r e t u r n realtype;};
v i r t u a l typeclass gettypeclass() c o n s t { r e t u r n Real; };
v i r t u a l i n t getsize() c o n s t { r e t u r n accuracy + exponent + 2; };
v i r t u a l i n t getaccuracy() c o n s t { r e t u r n accuracy; };
v i r t u a l i n t getexponent() c o n s t { r e t u r n exponent; };
A.2 C++, Lex and Yacc Code 91

v i r t u a l v o i d setexponentdigits()
{
char buf[10];
sprintf(buf,"%+d", t h i s ->exponent_value);
t h i s ->exponent = strlen(buf) - 1;
}
v i r t u a l b o o l issimple() c o n s t { r e t u r n TRUE; }
// code generation
v i r t u a l char *generate_decl();
// classify scalarset types as useful or useless
v i r t u a l v o i d setusefulness() {}
// classify variable type
v i r t u a l b o o l HasConstant() c o n s t { r e t u r n TRUE; }
v i r t u a l b o o l HasScalarsetVariable() c o n s t { r e t u r n FALSE; }
v i r t u a l b o o l HasScalarsetArrayOfFree() c o n s t { r e t u r n FALSE; }
v i r t u a l b o o l HasScalarsetArrayOfS() c o n s t { r e t u r n FALSE; }
v i r t u a l b o o l HasScalarsetLeaf() c o n s t { r e t u r n FALSE; }
v i r t u a l b o o l HasMultisetOfFree() c o n s t { r e t u r n FALSE; }
v i r t u a l b o o l HasMultisetOfScalarset() c o n s t { r e t u r n FALSE; }
// canonicalization code for symmetry
// this is just for compatibility, we won’t show the implementation
v i r t u a l v o i d generate_permute_function();
v i r t u a l v o i d generate_simple_canon_function(symmetryclass&);
v i r t u a l v o i d generate_canonicalize_function(symmetryclass&);
v i r t u a l v o i d generate_simple_limit_function(symmetryclass&);
v i r t u a l v o i d generate_array_limit_function(symmetryclass&);
v i r t u a l v o i d generate_limit_function(symmetryclass&);
v i r t u a l v o i d generate_multisetlimit_function(symmetryclass&);
v i r t u a l charlist * generate_scalarset_list(charlist * sl);
};

..
.

/********************
class constdecl
********************/
c l a s s constdecl: p u b l i c decl
{
i n t value;
double rvalue;
typedecl *type;
public:
// initializer
constdecl( i n t value, typedecl * type);
constdecl(expr * e);
// supporting routines
v i r t u a l decl_class getclass() c o n s t { r e t u r n Const; };
v i r t u a l typedecl *gettype() c o n s t { r e t u r n type; }
v i r t u a l i n t getvalue() c o n s t { r e t u r n value; }
v i r t u a l double getrvalue() c o n s t { r e t u r n rvalue; }
v i r t u a l designator *getdesignator(ste * origin) c o n s t
{
i f ((type->gettype()) == (realtype->gettype()))
r e t u r n new designator(origin, type, FALSE, TRUE, FALSE, rvalue);
else
r e t u r n new designator(origin, type, FALSE, TRUE, FALSE, value);
}
92 Chapter A. FHP-Murϕ: the Input Language

// code generation
v i r t u a l char *generate_decl();
};

A.2.9 M ODIFICATIONS TO DECL .C

..
.
/* class realtypedecl */
realtypedecl::realtypedecl( i n t ac, i n t ex)
: typedecl(),accuracy(ac),exponent_value(ex)
{
t h i s ->setexponentdigits(); // sets this->exponent
numbits = ( i n t ) ceil( t h i s ->getsize() / 2.0) * 8;
bitsalloc = numbits;
mu_type = "mu__real";
}
realtypedecl::realtypedecl(expr *ac, expr *ex)
: typedecl()
{
Error.CondError(!type_equal( ac->gettype(), inttype),
"Only integer accuracy allowed.");
Error.CondError(!type_equal( ex->gettype(), inttype),
"Only integer exponent allowed.");
Error.CondError(!ac->hasvalue(),"Accuracy must be constant.");
Error.CondError(!ex->hasvalue(),"Exponent must be constant.");
Error.CondError((ac->getvalue()<1) || (ac->getvalue()>DBL_DIG),
"Accuracy must be >= 1 and <= %d.",DBL_DIG);
Error.CondError((ex->getvalue()<10) ||
(ex ->getvalue()>DBL_MAX_10_EXP),
"Exponent max value must be between 10 and %d.",DBL_MAX_10_EXP);
t h i s ->accuracy = ac->getvalue();
t h i s ->exponent_value = ex->getvalue();
t h i s ->setexponentdigits();//IM: sets this->exponent
t h i s ->numbits = ( i n t ) ceil( t h i s ->getsize() / 2.0) * 8;
t h i s ->bitsalloc = t h i s ->numbits;
mu_type = "mu__real";
}

..
.
constdecl::constdecl(expr *e)
:decl(), type( e->gettype() )
{
Error.CondError(!e->hasvalue(), "CONST declaration requires constant
expression.");
i f (type_equal(e->gettype(),realtype))
rvalue = e->getrvalue(); // now, a constant may be a real one
else
value = e->getvalue();
}

..
.
typedecl *realtype = NULL; // AP: realtype’s initialization
A.2 C++, Lex and Yacc Code 93

A.2.10 M ODIFICATIONS TO EXPR . H

..
.

/********************
class expr
********************/
c l a s s expr: p u b l i c TNode
{
protected:
// variables
i n t value; /* value if it has one. */
double rvalue; // real value
typedecl *type; /* type of expression. */
b o o l constval, sideeffects;
// initializer used by subclasses.
expr( v o i d );
expr(typedecl * type, b o o l constval, b o o l sideeffects)
: type(type), constval(constval), sideeffects(sideeffects) {};
public:
// initializer
expr( c o n s t i n t value, typedecl * c o n s t type);
expr( c o n s t double value, typedecl * c o n s t type); // new constructor
// supporting routines
typedecl *gettype() c o n s t { r e t u r n type; };
v i r t u a l b o o l isquant() c o n s t { r e t u r n FALSE; };
v i r t u a l b o o l hasvalue() c o n s t { r e t u r n constval; };
// getrvalue() is similar to getvalue(), but returns double
double getrvalue() c o n s t { r e t u r n rvalue; };
v i r t u a l i n t getvalue() c o n s t { r e t u r n value; };
v i r t u a l b o o l islvalue() c o n s t { r e t u r n FALSE; };
v i r t u a l b o o l has_side_effects() c o n s t { r e t u r n sideeffects; };
v i r t u a l b o o l isdesignator() c o n s t { r e t u r n FALSE; };
v i r t u a l b o o l checkundefined() c o n s t { r e t u r n FALSE; };
v i r t u a l stecoll* used_stes() c o n s t { r e t u r n new stecoll; };
// code generation
v i r t u a l char *generate_code();
};
// mathematical functions on doubles
c l a s s mathexpr : p u b l i c expr
{
expr* arg1;
expr* arg2;
public:
enum mathexprtype {mylog, mylog10, myexp, mysin, mycos, mytan,
myfabs, myfloor, myceil, mysqrt, myfmod, mypow,
myasin, myacos, myatan, mysinh, mycosh, mytanh};
mathexprtype funtype;
mathexpr (expr* arg1,expr* arg2, i n t arg3);
mathexprtype getfuntype() { r e t u r n funtype;};
v i r t u a l char* generate_code();
};

..
.
94 Chapter A. FHP-Murϕ: the Input Language

/********************
class designator
-- represented as a left-linear tree.
********************/
c l a s s designator: p u b l i c expr
{
// variables
designator * left;
ste * origin; /* an identifier; */
expr * arrayref; /* an array reference. */
ste * fieldref; /* a field reference. */
b o o l lvalue;
// undefined
b o o l maybeundefined;
// class identifiers
enum designator_class { Base, ArrayRef, FieldRef };
designator_class dclass;
public:
// initializer
designator(ste * origin, typedecl * type, b o o l islvalue,
b o o l isconst, b o o l maybeundefined, i n t val = 0);
// this constructor is called by getdesignator() (constdecl)
designator(ste * origin, typedecl * type, b o o l islvalue,
b o o l isconst, b o o l maybeundefined, double val);
designator(designator *left, expr *ar);
designator(designator *left, lexid *fr);
// supporting routines
v i r t u a l b o o l islvalue() c o n s t { r e t u r n lvalue; };
ste *getbase( v o i d ) c o n s t ;
v i r t u a l b o o l isdesignator() c o n s t { r e t u r n TRUE; };
v i r t u a l b o o l checkundefined() c o n s t { r e t u r n maybeundefined; };
v i r t u a l stecoll* used_stes() c o n s t ;
// code generation
v i r t u a l char *generate_code();
};

A.2.11 M ODIFICATIONS TO EXPR .C

..
.
expr::expr( c o n s t double value, typedecl * c o n s t type) // new
constructor
:rvalue(value), type(type), constval(TRUE), sideeffects(FALSE)
{
}

A.2.12 M ODIFICATIONS TO CPP CODE .C

..
.
/* code for realtypedecl */
char *realtypedecl::generate_decl()
{
A.2 C++, Lex and Yacc Code 95

i f (!declared) {
/* Invent a name for the object, and a "mu_name" for
the declaration of the object in the generated code */
i f ( name == NULL ) {
name = tsprintf("_real_%d", tNum );
mu_name = tsprintf("mu_%s",name);
}
/* class name */
fprintf(codefile,
"class %s: public %s\n"
"{\n"
" public:\n"
" inline double operator=(double val) { return %s::operator=(va\
l); };\n"
" inline double operator=(const %s& val) { return %s::operator=\
((double) val); };\n"
" %s (char *name, int os): %s(%d,%d,%d,name, os){};\n"
" %s (void): %s(%d,%d,%d) {};\n"
" %s (double val): %s(%d,%d,%d,\"Parameter or function result.\
\", 0)\n"
" {\n"
" operator=(val);\n"
" };\n"
" char * Name() { return tsprintf(\"%%le\",value()); };\n"
,mu_name, mu_type, /* class name */
mu_type,
mu_name, mu_type,
mu_name, mu_type,accuracy,exponent_value, numbits,
mu_name, mu_type,accuracy,exponent_value, numbits,
mu_name, mu_type,accuracy,exponent_value, numbits
);
theprog->symmetry.generate_symmetry_function_decl();
fprintf(codefile,
" virtual void MultisetSort() {};\n"
" void print_statistic() {};\n"
"};\n\n"
);
fprintf(codefile, "/*** end of real decl ***/\n");
fprintf(codefile, "%s %s_undefined_var;\n\n", mu_name, mu_name);
declared = TRUE;
}
/* Should never use this as a return value expression */
r e t u r n "ERROR!";
}

..
.
/********************
code for constdecl
********************/
char *constdecl::generate_decl()
{
i f (!declared) {
i f (type_equal(type,realtype)) // real constant’s declaration
fprintf(codefile, "const double %s = %+le;\n", mu_name, rvalue);
else
fprintf(codefile, "const int %s = %d;\n", mu_name, value);
declared = TRUE;
}
r e t u r n "ERROR!";
}

..
.
96 Chapter A. FHP-Murϕ: the Input Language

/********************
code for expr
********************/
char *expr::generate_code()
{
i f (constval)
i f (type_equal(type,realtype))
r e t u r n tsprintf("%le",rvalue); // value of a real constant
else
r e t u r n tsprintf("%d",value);
else {
Error.Error("Internal: a basic expression that wasn’t a constant \
called expr::generate_code().");
r e t u r n "ERROR!";
}
}
// for math functions
char *mathexpr::generate_code()
{
i f (constval)
r e t u r n tsprintf("%le", rvalue);
s w i t c h (getfuntype())
{
c a s e mylog:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",log(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",log(arg1->getvalue()));
}
else
r e t u r n tsprintf("log((double)%s)",arg1->generate_code());
break;
}
c a s e mylog10:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",log10(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",log10(arg1->getvalue()));
}
else
r e t u r n tsprintf("log10((double)%s)",arg1->generate_code());
break;
}
c a s e myexp:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",exp(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",exp(arg1->getvalue()));
}
else
r e t u r n tsprintf("exp((double)%s)",arg1->generate_code());
break;
}
c a s e mysin:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
A.2 C++, Lex and Yacc Code 97

r e t u r n tsprintf("%le",sin(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",sin(arg1->getvalue()));
}
else
r e t u r n tsprintf("sin((double)%s)",arg1->generate_code());
break;
}
c a s e mycos:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",cos(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",cos(arg1->getvalue()));
}
else
r e t u r n tsprintf("cos((double)%s)",arg1->generate_code());
break;
}
c a s e mytan:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",tan(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",tan(arg1->getvalue()));
}
else
r e t u r n tsprintf("tan((double)%s)",arg1->generate_code());
break;
}
c a s e myfabs:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",fabs(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",fabs(arg1->getvalue()));
}
else
r e t u r n tsprintf("fabs((double)%s)",arg1->generate_code());
break;
}
c a s e myfloor:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",floor(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",floor(arg1->getvalue()));
}
else
r e t u r n tsprintf("floor((double)%s)",arg1->generate_code());
break;
}
c a s e myceil:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",ceil(arg1->getrvalue()));
else
98 Chapter A. FHP-Murϕ: the Input Language

r e t u r n tsprintf("%le",ceil(arg1->getvalue()));
}
else
r e t u r n tsprintf("ceil((double)%s)",arg1->generate_code());
break;
}
c a s e mysqrt:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",sqrt(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",sqrt(arg1->getvalue()));
}
else
r e t u r n tsprintf("sqrt((double)%s)",arg1->generate_code());
break;
}
c a s e myfmod:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
{
i f (type_equal(arg2->gettype(),realtype))
r e t u r n tsprintf("%le",fmod(arg1->getrvalue(),
arg2->getrvalue()));
else
r e t u r n tsprintf("%le",fmod(arg1->getrvalue(),
arg2->getvalue()));
}
else
{
i f (type_equal(arg2->gettype(),realtype))
r e t u r n tsprintf("%le",fmod(arg1->getvalue(),
arg2->getrvalue()));
else
r e t u r n tsprintf("%le",fmod(arg1->getvalue(),
arg2->getvalue()));
}
}
else
r e t u r n tsprintf("fmod((double)%s,(double)%s)",
arg1->generate_code(), arg2->generate_code());
break;
}
c a s e mypow:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
{
i f (type_equal(arg2->gettype(),realtype))
r e t u r n tsprintf("%le",pow(arg1->getrvalue(),
arg2->getrvalue()));
else
r e t u r n tsprintf("%le",pow(arg1->getrvalue(),
arg2->getvalue()));
}
else
{
i f (type_equal(arg2->gettype(),realtype))
r e t u r n tsprintf("%le",pow(arg1->getvalue(),
arg2->getrvalue()));
else
r e t u r n tsprintf("%le",pow(arg1->getvalue(),
arg2->getvalue()));
A.2 C++, Lex and Yacc Code 99

}
}
else
r e t u r n tsprintf("pow((double)%s,(double)%s)",
arg1->generate_code(), arg2->generate_code());
break;
}
c a s e myasin:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",asin(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",asin(arg1->getvalue()));
}
else
r e t u r n tsprintf("asin((double)%s)",arg1->generate_code());
break;
}
c a s e myacos:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",acos(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",acos(arg1->getvalue()));
}
else
r e t u r n tsprintf("acos((double)%s)",arg1->generate_code());
break;
}
c a s e myatan:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",atan(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",atan(arg1->getvalue()));
}
else
r e t u r n tsprintf("atan((double)%s)",arg1->generate_code());
break;
}
c a s e mysinh:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",sinh(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",sinh(arg1->getvalue()));
}
else
r e t u r n tsprintf("sinh((double)%s)",arg1->generate_code());
break;
}
c a s e mycosh:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",cosh(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",cosh(arg1->getvalue()));
100 Chapter A. FHP-Murϕ: the Input Language

}
else
r e t u r n tsprintf("cosh((double)%s)",arg1->generate_code());
break;
}
c a s e mytanh:
{
i f (constval)
{
i f (type_equal(arg1->gettype(),realtype))
r e t u r n tsprintf("%le",tanh(arg1->getrvalue()));
else
r e t u r n tsprintf("%le",tanh(arg1->getvalue()));
}
else
r e t u r n tsprintf("tanh((double)%s)",arg1->generate_code());
break;
}
}
}

..
.

/********************
code for arithexpr
********************/
char *arithexpr::generate_code()
{
i f (constval)
i f (type_equal(type,realtype))
// value of a real arithmetic expression (+,-)
r e t u r n tsprintf("%le", rvalue);
else
r e t u r n tsprintf("%d", value);
else {
s w i t c h (op) {
c a s e ’+’:
r e t u r n tsprintf("(%s) + (%s)",
left->generate_code(),
right->generate_code());
c a s e ’-’:
r e t u r n tsprintf("(%s) - (%s)",
left->generate_code(),
right->generate_code());
default:
Error.Error("Internal: bad operator in arithexpr::\
generate_code()");
r e t u r n "ERROR!";
}
}
};
/********************
code for unexpr
********************/
char *unexpr::generate_code()
{
i f (constval)
i f (type_equal(type,realtype))
// value of a real unary expression
r e t u r n tsprintf("%le", rvalue);
else
r e t u r n tsprintf("%d", value);
else {
s w i t c h (op) {
A.2 C++, Lex and Yacc Code 101

c a s e ’+’:
r e t u r n left->generate_code();
c a s e ’-’:
r e t u r n tsprintf(" - (%s)",
left->generate_code());
default:
Error.Error("Internal: bad operator in arithexpr::\
generate_code()");
r e t u r n "ERROR!";
}
}
};
/********************
code for mulexpr
********************/
char *mulexpr::generate_code()
{
i f (constval)
i f (type_equal(type,realtype))
// value of a real arithmetic expression (*,/)
r e t u r n tsprintf("%le", rvalue);
else
r e t u r n tsprintf("%d", value);
else {
s w i t c h (op) {
c a s e ’*’:
r e t u r n tsprintf("(%s) * (%s)",
left->generate_code(),
right->generate_code());
c a s e ’/’:
r e t u r n tsprintf("(%s) / (%s)",
left->generate_code(),
right->generate_code());
c a s e ’%’:
/* doubled % to accomodate printf. */
r e t u r n tsprintf("(%s) %% (%s)",
left->generate_code(),
right->generate_code());
default:
Error.Error("Internal: bad operator in mulexpr::\
generate_code()");
r e t u r n "ERROR!";
}
}
}

..
.
/********************
code for simplerule
********************/
char *simplerule::generate_code()
{
fprintf(codefile,
"/******************** RuleBase%d ********************/\n"
"class RuleBase%d\n"
"{\n"
"public:\n",
rulenumber,
rulenumber
);
// priority function, added by Uli
fprintf(codefile,
" int Priority()\n"
102 Chapter A. FHP-Murϕ: the Input Language

" {\n"
" return %d;\n"
" }\n",
priority
);
// generate Name(r)
fprintf(codefile,
" char * Name(unsigned r)\n"
" {\n"
);
generate_rule_params_assignment( enclosures );
fprintf(codefile,
" return tsprintf(\"%s", name);
generate_rule_params_printf( enclosures );
fprintf(codefile,"\"");
generate_rule_params_name( enclosures );
fprintf(codefile, ");\n }\n");
// generate Condition(r)
fprintf(codefile,
" bool Condition(unsigned r)\n"
" {\n"
);
generate_rule_params_assignment( enclosures );
generate_rule_params_choose( enclosures );
generate_rule_aliases( enclosures );
i f (args->pmurphik)
{
// condition return for probabilistic rules is "return prob > 0"
i f (condition->hasvalue())
fprintf(codefile,
" return %d;\n",
condition->getrvalue() > 0);
else
fprintf(codefile,
" return (%s > 0);\n",
condition->generate_code());
}
else
fprintf(codefile,
" return %s;\n",
condition->generate_code());
fprintf(codefile,
" }\n"
"\n"
);
// generate NextRule(r)
i f (args->pmurphik && !args->prob_rules && !args->probequalk)
/* it is necessary to have an additional output parameter for the
probability of the rule */
fprintf(codefile,
" void NextRule(unsigned & what_rule, double & what_prob)\n"
" {\n"
" unsigned r = what_rule - %d;\n",
maxrulenumber - getsize()
);
else
fprintf(codefile,
" void NextRule(unsigned & what_rule)\n"
" {\n"
" unsigned r = what_rule - %d;\n",
maxrulenumber - getsize()
);
generate_rule_params_assignment( enclosures );
fprintf(codefile," while (what_rule < %d ",
A.2 C++, Lex and Yacc Code 103

maxrulenumber );
generate_rule_params_choose_exist( enclosures );
fprintf(codefile,")\n {\n");
// Check if ‘‘dependent’’ choose parameters are there
fprintf(codefile," if ( ( TRUE ");
generate_rule_params_choose_all_in( dep_choose );
fprintf(codefile," ) ) {\n");
// Check condition
generate_rule_aliases( enclosures );
i f (args->pmurphik && !args->probequalk)
{
i f (condition->hasvalue())
{
fprintf(codefile,
" what_prob = %s;\n"
" if (%d) {\n",
condition->generate_code(),
condition->getrvalue() > 0 );
}
else
{
fprintf(codefile,
" what_prob = %s;\n"
" if ((what_prob < 0) || (what_prob > 1))\n"
" { \n"
" Error.Error(\"Condition of probabilistic rule %%s is not\
in range [0,1].\", this->Name(what_rule));\n"
" return;\n"
" }\n",condition->generate_code());
fprintf(codefile," if (what_prob > 0) {\n");
}
}
else
fprintf(codefile," if (%s) {\n",
condition->generate_code() );
// Check is ‘‘independent’’ choose parameters are there
fprintf(codefile," if ( ( TRUE ");
generate_rule_params_choose_all_in( indep_choose );
fprintf(codefile," ) )\n");
fprintf(codefile," return;\n");
fprintf(codefile," else\n");
fprintf(codefile," what_rule++;\n");
fprintf(codefile," }\n");
fprintf(codefile," else\n");
fprintf(codefile," what_rule += %d;\n",
indep_card);
fprintf(codefile," }\n");
fprintf(codefile," else\n");
fprintf(codefile," what_rule += %d;\n",
indep_card);
// End of Vitaly’s hacks
fprintf(codefile," r = what_rule - %d;\n",
maxrulenumber - getsize());
generate_rule_params_simple_assignment( enclosures );
fprintf(codefile,
" }\n"
" }\n\n");
// generate Code(r)
fprintf(codefile,
104 Chapter A. FHP-Murϕ: the Input Language

" void Code(unsigned r)\n"


" {\n"
);
generate_rule_params_assignment( enclosures );
generate_rule_aliases( enclosures );
locals->generate_decls();
f o r (stmt *b = body; b != NULL; b = b-> n e x t )
b->generate_code();
fprintf(codefile,
" };\n"
"\n"
);
// generate Fair()
i f (unfair)
fprintf(codefile,
" bool UnFair()\n"
" { return TRUE; }\n"
);
else
fprintf(codefile,
" bool UnFair()\n"
" { return FALSE; }\n"
);
// end declaration
fprintf(codefile,
"};\n"
);
r e t u r n "ERROR!";
}

..
.
/********************
code for pctl
********************/
v o i d pctl::generate_atomic_subformulas(pctl *formula)
{
i f (formula == NULL)
r e t u r n;
i f (formula->pctltype == AP_PCTL)
{
fprintf(codefile, "bool mu_pctl_a_p%d()\n{\n", formula->code);
char *temp = formula->atomic_proposition->generate_code();
fprintf(codefile, "return %s;\n}\n", temp);
}
else
{
generate_atomic_subformulas(formula->subformula1);
generate_atomic_subformulas(formula->subformula2);
}
}
c o n s t char *pctl::getstring(pctl_type t)
{
s w i t c h (t)
{
c a s e UNTIL_PCTL: r e t u r n "UNTIL_PCTL";
c a s e NEXT_PCTL: r e t u r n "NEXT_PCTL";
c a s e AP_PCTL: r e t u r n "AP_PCTL";
c a s e AND_PCTL: r e t u r n "AND_PCTL";
c a s e OR_PCTL: r e t u r n "OR_PCTL";
c a s e IMPL_PCTL: r e t u r n "IMPL_PCTL";
c a s e NOT_PCTL: r e t u r n "NOT_PCTL";
A.2 C++, Lex and Yacc Code 105

}
}
c o n s t char *pctl::getstring(pctl_ord o)
{
s w i t c h (o)
{
c a s e PCTL_L: r e t u r n "PCTL_L";
c a s e PCTL_LEQ: r e t u r n "PCTL_LEQ";
c a s e PCTL_G: r e t u r n "PCTL_G";
c a s e PCTL_GEQ: r e t u r n "PCTL_GEQ";
}
}
v o i d pctl::generate_non_atomic_subformulas(pctl *formula)
{
i f (formula == NULL)
return;
generate_non_atomic_subformulas(formula->subformula1);
generate_non_atomic_subformulas(formula->subformula2);
i f (formula == t h i s )
fprintf(codefile, "const pctlformrec mu_pctl_formula = {\n");
else
fprintf(codefile, "const pctlformrec mu_pctl_formula%d = {\n",
formula->code);
fprintf(codefile, "%s, ", getstring(formula->pctltype));
i f (formula->pctltype != AP_PCTL)
fprintf(codefile, "NULL, ");
else
fprintf(codefile, "&mu_pctl_a_p%d, ", formula->code);
i f (formula->pctltype != NEXT_PCTL &&
formula->pctltype != UNTIL_PCTL)
fprintf(codefile, "PCTL_L, 0.0, ");
else
fprintf(codefile, "%s, %lf, ", getstring(formula->op),
formula->prob_bound);
fprintf(codefile, "%d, ", formula->until_bound);
i f (formula->subformula1 == NULL)
fprintf(codefile, "NULL, ");
else
fprintf(codefile, "&mu_pctl_formula%d, ",
formula->subformula1->code);
i f (formula->subformula2 == NULL)
fprintf(codefile, "NULL");
else
fprintf(codefile, "&mu_pctl_formula%d",
formula->subformula2->code);
fprintf(codefile, "};\n");
}
char *pctl::generate_code()
{
generate_atomic_subformulas( t h i s );
generate_non_atomic_subformulas( t h i s );
r e t u r n "ERROR!";
}

..
.
i n t generate_ruleset()
{
i n t i, r, max_r;
simplerule * sr;
r = 0;
106 Chapter A. FHP-Murϕ: the Input Language

max_r = 0;
f o r (sr = simplerule::SimpleRuleList;
sr != NULL; sr = sr->NextSimpleRule)
{
i f (sr->getclass() == rule::Simple && sr != error_rule)
{
sr->rulenumber = r++;
max_r += sr->getsize(); sr->maxrulenumber = max_r;
sr->generate_code();
}
}
r = 0;
fprintf(codefile,
"class NextStateGenerator\n"
"{\n"
);
f o r (sr = simplerule::SimpleRuleList;
sr != NULL; sr = sr->NextSimpleRule)
{
i f (sr->getclass() == rule::Simple && sr != error_rule)
{
fprintf(codefile," RuleBase%d R%d;\n",r,r);
r++;
}
}
fprintf(codefile,"public:\n");
// generate SetNextEnabledRule(r)
i = 0; r = 0;
i f (!args->pmurphik || args->probequalk || args->prob_rules)
/* in pmurphik mode, SetNextEnabledRule will return also the
probability of the rule */
fprintf(codefile,
"void SetNextEnabledRule(unsigned & what_rule)\n"
"{\n"
" category = CONDITION;\n"
);
else
fprintf(codefile,
"void SetNextEnabledRule(unsigned & what_rule, double & \
what_prob)\n"
"{\n"
" category = CONDITION;\n"
);
f o r (sr = simplerule::SimpleRuleList;
sr != NULL; sr = sr->NextSimpleRule)
{
i f (sr->getclass() == rule::Simple && sr != error_rule)
{
i f (args->pmurphik && !args->prob_rules && !args->probequalk)
{
i f (i != 0)
fprintf(codefile,
" if (what_rule>=%d && what_rule<%d)\n"
" { R%d.NextRule(what_rule, what_prob);\n"
" if (what_rule<%d) return; }\n",
i, i+sr->getsize(), r, i+sr->getsize());
else
fprintf(codefile,
" if (what_rule<%d)\n"
" { R%d.NextRule(what_rule, what_prob);\n"
" if (what_rule<%d) return; }\n",
i+sr->getsize(), r, i+sr->getsize());
r++; i+=sr->getsize();
}
else
{
A.2 C++, Lex and Yacc Code 107

i f (i != 0)
fprintf(codefile,
" if (what_rule>=%d && what_rule<%d)\n"
" { R%d.NextRule(what_rule);\n"
" if (what_rule<%d) return; }\n",
i, i+sr->getsize(), r, i+sr->getsize());
else
fprintf(codefile,
" if (what_rule<%d)\n"
" { R%d.NextRule(what_rule);\n"
" if (what_rule<%d) return; }\n",
i+sr->getsize(), r, i+sr->getsize());
r++; i+=sr->getsize();
}
}
}
fprintf(codefile,
"}\n"
);
// generate Condition(r)
fprintf(codefile,
"bool Condition(unsigned r)\n"
"{\n"
" category = CONDITION;\n"
);
i = 0; r = 0;
f o r (sr = simplerule::SimpleRuleList;
sr != NULL; sr = sr->NextSimpleRule)
{
i f (sr->getclass() == rule::Simple && sr != error_rule)
{
i f (i != 0)
fprintf(codefile,
" if (r>=%d && r<=%d) return R%d.Condition(r-%d);\n",
i, i+sr->getsize()-1, r, i);
else
fprintf(codefile,
" if (r<=%d) return R%d.Condition(r-%d);\n",
i+sr->getsize()-1, r, i);
r++;
i+=sr->getsize();
}
}
fprintf(codefile,
"Error.Notrace(\"Internal: NextStateGenerator -- checking condit\
ion for nonexisting rule.\");\n"
"}\n"
);
// generate Code(r)
fprintf(codefile,
"void Code(unsigned r)\n"
"{\n"
);
i = 0; r = 0;
f o r (sr = simplerule::SimpleRuleList;
sr != NULL; sr = sr->NextSimpleRule)
{
i f (sr->getclass() == rule::Simple && sr != error_rule)
{
i f (i != 0)
fprintf(codefile,
" if (r>=%d && r<=%d) { R%d.Code(r-%d); return; } \n",
i, i+sr->getsize()-1, r, i);
else
fprintf(codefile,
" if (r<=%d) { R%d.Code(r-%d); return; } \n",
108 Chapter A. FHP-Murϕ: the Input Language

i+sr->getsize()-1, r, i);
r++;
i+=sr->getsize();
}
}
fprintf(codefile,
"}\n"
);
// generate Priority, added by Uli
fprintf(codefile,
"int Priority(unsigned short r)\n"
"{\n"
);
i = 0; r = 0;
f o r (sr = simplerule::SimpleRuleList;
sr != NULL; sr = sr->NextSimpleRule)
{
i f (sr->getclass() == rule::Simple && sr != error_rule)
{
i f (i != 0)
fprintf(codefile,
" if (r>=%d && r<=%d) { return R%d.Priority(); } \n",
i, i+sr->getsize()-1, r);
else
fprintf(codefile,
" if (r<=%d) { return R%d.Priority(); } \n",
i+sr->getsize()-1, r);
r++;
i+=sr->getsize();
}
}
fprintf(codefile,
"}\n"
);
// generate Name(r)
fprintf(codefile,
"char * Name(unsigned r)\n"
"{\n"
);
i = 0; r = 0;
f o r (sr = simplerule::SimpleRuleList;
sr != NULL; sr = sr->NextSimpleRule)
{
i f (sr->getclass() == rule::Simple && sr != error_rule)
{
i f (i != 0)
fprintf(codefile,
" if (r>=%d && r<=%d) return R%d.Name(r-%d);\n",
i, i+sr->getsize()-1, r, i);
else
fprintf(codefile,
" if (r<=%d) return R%d.Name(r-%d);\n",
i+sr->getsize()-1, r, i);
r++;
i+=sr->getsize();
}
}
fprintf(codefile, " return NULL;\n"); // added by Uli
fprintf(codefile,
"}\n"
);
fprintf(codefile,
"};\n"
);
r e t u r n i;
}
A.2 C++, Lex and Yacc Code 109

..
.

char *program::generate_code()
{
i n t count;
// header
fprintf(codefile,"/******************************\n");
fprintf(codefile," Program \"%s\" compiled by \"%s\"\n",
args->filename, MURPHI_VERSION );
fprintf(codefile, "\n");
fprintf(codefile," Murphi Last Modified Date: \"%s\"\n",
LAST_DATE);
fprintf(codefile," Murphi Last Compiled date: \"%s\"\n",
__DATE__);
fprintf(codefile," ******************************/\n");
fprintf(codefile, "\n");
// record the date of the compiler.
fprintf(codefile,"/********************\n Parameter\n");
fprintf(codefile," ********************/\n");
i f (args->hash_disk || args->hash_cache || args->pmurphik ||
args->probequalk)
fprintf(codefile,"#define MURPHI_VERSION \"%s\"\n",
MURPHI_VERSION);
else
fprintf(codefile,"#define MURPHI_VERSION \"Murphi Release \
3.1\"\n");
fprintf(codefile,"#define MURPHI_DATE \"%s\"\n", LAST_DATE);
fprintf(codefile, "#define PROTOCOL_NAME \""); // added by Uli
f o r ( i n t i=0, l=strlen(args->filename)-2; i<l; i++)
fputc(args->filename[i], codefile);
fprintf(codefile, "\"\n#define BITS_IN_WORLD %d\n",
bits_in_world);
i f (args->no_compression)
fprintf(codefile, "#define ALIGN\n");
i f (args->hash_compression)
fprintf(codefile, "#define HASHC\n");
i f (args->hash_disk)
fprintf(codefile, "#define DISK_MURPHI\n");
i f (args->hash_cache)
fprintf(codefile, "#define CACHED_MURPHI\n");
i f (args->pmurphik && !args->prob_rules)
fprintf(codefile, "#define PMURPHIK\n");
i f (args->probequalk)
fprintf(codefile, "#define PROBEQUALK\n");
i f (args->bpctl)
fprintf(codefile, "#define BPCTL\n");
fprintf(codefile, "\n");
// include prolog
fprintf(codefile,"/********************\n Include\n");
fprintf(codefile," ********************/\n");
fprintf(codefile,"#include \"mu_prolog.inc\"\n");
/* generate dependent stuff. */
fprintf(codefile,"\n/********************\n Decl declaration\n");
fprintf(codefile," ********************/\n\n");
i f (args->prob_rules)
fprintf(codefile,"double what_prob;\n\n");
// typedecl declaration -- added to fixed a bug
i f (typedecl::origin != NULL)
typedecl::origin->generate_all_decl();
/* globals->generate_decls() gets done by
procedures->generate_decls */
110 Chapter A. FHP-Murϕ: the Input Language

procedures->generate_decls();
fprintf(codefile,"\n\n\n");
// generate the world
fprintf(codefile,"\n/********************\n The world\n");
fprintf(codefile," ********************/\n");
make_world(globals);
fprintf(codefile,"\n");
// generate the codes for each rule
fprintf(codefile,"\n/********************\n Rule declarations\n");
fprintf(codefile," ********************/\n");
// for ( rule *r = rules;
// r != NULL;
// r = r->next )
// r->generate_code();
// fprintf(codefile,"\n\n\n");
//
// /* generate the lists of rules, startstate, and invariants. */
// // generate the lists of rules
// fprintf(codefile,"\n/********************\n Rule records\n");
// fprintf(codefile," ********************/\n");
// fprintf(codefile, "const rulerec rules[] = {\n");
// count = rulelist->print_rules();
// fprintf(codefile,"};\n");
count = generate_ruleset();
fprintf(codefile, "const unsigned numrules = %d;\n", count );
fprintf(codefile,"\n/********************\n parameter\n");
fprintf(codefile," ********************/\n");
fprintf(codefile, "#define RULES_IN_WORLD %d\n", count );
fprintf(codefile,"\n");
// generate the lists of startstate
fprintf(codefile,"\n/********************\n Startstate records\n");
fprintf(codefile," ********************/\n");
// fprintf(codefile, "const rulerec startstates[] = {\n");
// count = startstatelist->print_rules();
// fprintf(codefile,"};\n");
count = generate_startstates();
fprintf(codefile, "const rulerec startstates[] = {\n");
simplerule * sr; /* this is to have probabilities on startstates */
f o r (sr = simplerule::SimpleRuleList;
sr != NULL; sr = sr->NextSimpleRule)
{
i f (sr->getclass() == rule::Startstate)
{
i f ((args->pmurphik && !args->prob_rules) || args->bpctl ||
args->probequalk)
fprintf(codefile,"{ NULL, NULL, NULL, FALSE, 1.0},\n");
else
fprintf(codefile,"{ NULL, NULL, NULL, FALSE},\n");
}
}
fprintf(codefile,"};\n");
fprintf(codefile, "unsigned short StartStateManager::numstartstate\
s = %d;\n", count);
i f (!args->bpctl && !args->pmurphik)
{
// generate the lists of invariants
fprintf(codefile,"\n/********************\n Invariant records\
\n");
fprintf(codefile," ********************/\n");
( v o i d ) generate_invariants();
fprintf(codefile, "const rulerec invariants[] = {\n");
A.2 C++, Lex and Yacc Code 111

count = invariantlist->print_rules();
fprintf(codefile,"};\n");
fprintf(codefile, "const unsigned short numinvariants = %d;\n",
count );
// generate the special true function for liveness
fprintf(codefile, "\n/******************/\n");
fprintf(codefile, "bool mu__true_live() { return TRUE; }");
fprintf(codefile, "\n/******************/\n");
// generate the lists of livenesses
fprintf(codefile,"\n/********************\n Liveness records\n");
fprintf(codefile," ********************/\n");
fprintf(codefile, "const liverec livenesses[] = {\n");
count = livenesslist->print_liverules();
fprintf(codefile,"};\n");
fprintf(codefile, "const unsigned short numlivenesses = %d;\n",
count);
// if there is liveness property, define LIVENESS_CHECKING
i f (count>0)
fprintf(codefile, "\n#define LIVENESS_CHECKING\n");
// generate the lists of fairstates
fprintf(codefile,"\n/********************\n Fairstates records\
\n");
fprintf(codefile," ********************/\n");
fprintf(codefile, "const rulerec fairnesses[] = {\n");
count = fairnesslist->print_rules();
fprintf(codefile,"};\n");
fprintf(codefile, "const unsigned short numfairnesses = %d;\n",
count);
}
else //IM: PCTL formula code
{
fprintf(codefile,"\n/********************\n PCTL formulas record\
s\n");
fprintf(codefile," ********************/\n");
( v o i d ) theprog->pctl_form->generate_code();
}
// generate normalization/canonicalization function for scalarset
fprintf(codefile,"\n/********************\n Normal/Canonicalizati\
on for scalarset\n");
fprintf(codefile," ********************/\n");
symmetry.generate_code(globals);
// include prolog
fprintf(codefile,"\n/********************\n Include\n");
fprintf(codefile," ********************/\n");
/* I\’ve decided to just #include it, so that I can foist off the
problem of finding the file to be included on the c++
compiler. */
/* But the filename still should not be hardcoded. */
fprintf(codefile,"#include \"mu_epilog.inc\"\n");
r e t u r n "ERROR!";
}
A PPENDIX B

FHP-M URϕ: THE V ERIFIER

In this appendix, we will show the code implementing the two algorithms of Chapters 3 and 4. This has
been done by modifying the files in the include directory of the original Murϕ distribution (see the
introductive part of Appendix A); moreover, two files have been added. In FHP-Murϕ, the modified and
added files are paced in a separate subdirectory of include, which is named bpctl, with the exception of
mu real.h, mu real.C, mu prolog.inc and mu epilog.inc, which are stored in the include
directory.

B.1 C++ C ODE

As for Appendix A, we give only the modified (and added) files, together with the modifications context.

B.1.1 M ODIFICATIONS TO MU PROLOG . INC

..
.
# i f defined(PMURPHIK)
#ifndef gPercentActiveStates
#define gPercentActiveStates 0.5
#endif
#else
#define gPercentActiveStates 0.1
#endif
// for use in tsprintf
# d e f i n e BUFFER_SIZE 1024
/****************************************
Release information
****************************************/
/* last update */
# d e f i n e INCLUDE_FILE_DATE "Oct 20 2001"
/* Murphi Version Number */
# i f (defined(PMURPHIK) || defined(CACHED_MURPHI) ||
defined(DISK_MURPHI) || defined(BPCTL))
#define INCLUDE_FILE_VERSION "Cached Murphi Release 5.2"
#else
#define INCLUDE_FILE_VERSION "Murphi Release 3.1"
#endif
/****************************************
Cached Murphi constants
****************************************/
/* max length of collision chain in cache */
# d e f i n e MAX_HT_CHAIN_LENGTH 11

113
114 Chapter B. FHP-Murϕ: the Verifier

/* max length of collision chain in cache when hash compaction is


used */
# d e f i n e MAX_HT_CHAIN_LENGTH_HC 13
/* length of split file segments */
#define SPLITFILE_LEN (1024 * 0x100000L)
/****************************************
main headers
****************************************/
# i f defined(PMURPHIK)
# i f defined(BPCTL)
#include "bpctl/mu_verifier.h"
#else
#include "pmurphik/mu_verifier.h"
#endif
#else
#include "mu_verifier.h"
#endif
/****************************************
other headers
****************************************/
# i f d e f HASHC
# i n c l u d e "mu_hash.h"
#endif
# i n c l u d e "mu_sym.h"
# i f defined(DISK_MURPHI)
#include "murphi.disk/mu_util.h"
#else
# i f defined(CACHED_MURPHI)
#include "murphi.cache/mu_util.h"
#else
# i f defined(PMURPHIK)
#include "pmurphik/mu_util.h"
#else
#include "mu_util.h"
#endif
#endif
#endif
# i f defined(DISK_MURPHI)
#include "murphi.disk/mu_io.h"
#else
# i f defined(CACHED_MURPHI)
#include "murphi.cache/mu_io.h"
#else
# i f defined(PMURPHIK)
# i f defined(BPCTL)
#include "bpctl/mu_io.h"
#else
#include "pmurphik/mu_io.h"
#endif
#else
#include "mu_io.h"
#endif
#endif
#endif
# i f defined(DISK_MURPHI)
#include "murphi.disk/mu_state.h"
#include "murphi.disk/splitFile.h"
#include "murphi.disk/mu_filterqueue.h"
#include "murphi.disk/mu_system.h"
#else
B.1 C++ Code 115

# i f defined(CACHED_MURPHI)
#include "murphi.cache/mu_state.h"
#include "murphi.cache/mu_system.h"
#else
# i f defined(PMURPHIK)
# i f defined(BPCTL)
#include "splitFile.h"
#include "bpctl/mu_state.h"
#include "bpctl/mu_probstack.h"
#include "bpctl/mu_system.h"
#else
#include "pmurphik/mu_state.h"
#include "pmurphik/mu_system.h"
#endif
#else
#include "mu_state.h"
#include "mu_system.h"
#endif
#endif
#endif
/****************************************
real numbers extension
****************************************/
# i n c l u d e "mu_real.h" //im

B.1.2 M ODIFICATIONS TO MU EPILOG . ING

..
.
/****************************************
header that depend on constants
which is generated in the middle of the compiled program:
RULES_IN_WORLD
****************************************/
# i f defined(PMURPHIK)
# i n c l u d e "pmurphik/mu_util_dep.h"
#else
# i n c l u d e "mu_util_dep.h"
#endif
/****************************************
supporting routines
****************************************/
# i f d e f HASHC
# i n c l u d e "mu_hash.C"
#endif
# i f defined(DISK_MURPHI)
#include "murphi.disk/mu_util.C"
#else
# i f defined(PMURPHIK)
# i f defined(BPCTL)
#include "bpctl/mu_util.C"
#else
#include "pmurphik/mu_util.C"
#endif
#else
#include "mu_util.C"
#endif
#endif
# i f defined(DISK_MURPHI)
116 Chapter B. FHP-Murϕ: the Verifier

#include "murphi.disk/mu_io.C"
#else
# i f defined(CACHED_MURPHI)
#include "murphi.cache/mu_io.C"
#else
# i f defined(PMURPHIK)
# i f defined(BPCTL)
#include "bpctl/mu_io.C"
#else
#include "pmurphik/mu_io.C"
#endif
#else
#include "mu_io.C"
#endif
#endif
#endif
# i n c l u d e "mu_sym.C"
# i f defined(DISK_MURPHI)
#include "murphi.disk/mu_state.C"
#include "murphi.disk/splitFile.C"
#include "murphi.disk/mu_filterqueue.C"
#include "murphi.disk/mu_system.C"
#else
# i f defined(CACHED_MURPHI)
#include "murphi.cache/mu_state.C"
#include "murphi.cache/mu_system.C"
#else
# i f defined(PMURPHIK)
// #include "pmurphik/splitFile.C"
# i f defined(BPCTL)
#include "splitFile.C"
#include "bpctl/mu_state.C"
#include "bpctl/mu_probstack.C"
#include "bpctl/mu_system.C"
#else
#include "pmurphik/mu_state.C"
#include "pmurphik/mu_probqueue.C"
#include "pmurphik/mu_system.C"
#endif
#else
#include "mu_state.C"
#include "mu_system.C"
#endif
#endif
#endif
/****************************************
real numbers extension
****************************************/
# i n c l u d e "mu_real.C" //im
/****************************************
main routines
****************************************/
# i f defined(BPCTL)
# i n c l u d e "bpctl/mu_verifier.C"
#else
# i n c l u d e "mu_verifier.C"
#endif
B.1 C++ Code 117

B.1.3 M ODIFICATIONS TO MU REAL . H

..
.
# i f n d e f _REALCLASS_
# d e f i n e _REALCLASS_
/* class mu__real’s declaration (base for real numbers) */
c l a s s mu__real {
s t a t i c double undef_value; //undef_value for real type
v o i d o p e r a t o r=(mu__real&);
protected:
// accuracy is the number of decimal digit used to represent a real
// exponent is the number of digit used for the exponent
i n t accuracy,exponent;
// maximum positive exponent value (negative is -exponent_value+1)
i n t exponent_value;
// if TRUE global variable, otherwise local
b o o l in_world;
// offset in the state vector, size (both in bits)
i n t offset,size;
// size in byte
i n t size_in_byte;
// format_string_1 contains the format string used for write a
// real number to the state vector
// Ex: if accuracy = 6 then format_string_1 = "%+.5Le".
char format_string_1[10];
// format_string_2 contains the format string used for print
// the value of a real variable (see function Print())
// Ex: if name = "x" and accuracy = 6 then
// format_string_2 = "x:%+.5Le"
char format_string_2[300];
// Note: the size of format_string_2 also depends on
// the length of an identifier
# i f d e f ALIGN
i n t byteOffset; // offset in state vector in bytes
#endif
b o o l initialized; // whether it is initialized in the startstate
// Uli: seems not to be used any more
double cvalue; // contains value of local variable
public:
char *name; // name of the variable
char longname[BUFFER_SIZE / 4];
// constructors
mu__real( i n t accuracy, i n t exponent, i n t size,char *n, i n t os )
: accuracy(accuracy),
exponent_value(exponent),size(size),initialized(FALSE)
//exponent given to this->exponent_value and not to exponent
{
setexponentdigits(); //sets this->exponent
set_formats_string();
set_self(n, os);
size_in_byte = div(size,8).quot;
undefined();
};
mu__real( i n t accuracy, i n t exponent, i n t size)
: accuracy(accuracy), exponent_value(exponent), size(size),
initialized(FALSE)
//exponent given to this->exponent_value and not to exponent
118 Chapter B. FHP-Murϕ: the Verifier

{
setexponentdigits();//sets this->exponent
set_formats_string();
set_self(NULL,0);
size_in_byte = div(size,8).quot;
undefined();
};
// Uli: - seems not to be used any more
// - however, I left it here since it does the correct things
mu__real( i n t accuracy, i n t precision, i n t size,double val)
: accuracy(accuracy), exponent_value(exponent), size(size)
//exponent given to this->exponent_value and not to exponent
{
setexponentdigits();//sets this->exponent
set_self("Parameter or function result", 0);
set_formats_string();
o p e r a t o r=(val);
size_in_byte = div(size,8).quot;
};
// Uli: this constructor is called implicitly for function results
// by the code generated by the Murphi compiler
mu__real( c o n s t mu__real &src)
: accuracy(src.accuracy), exponent_value(src.exponent_value),
size(src.size), in_world(FALSE)
//exponent given to this->exponent_value and not to exponent
{
setexponentdigits();//sets this->exponent
set_formats_string(); //fixed, was after the call to value
set_self("Function result", 0);
value(src.value()); // value() returning undefined values
size_in_byte = div(size,8).quot;
};
// a destructor
˜mu__real() {};
v o i d setexponentdigits() //number of digits of exponent_value
{
char buf[10];
sprintf(buf,"%+d",exponent_value);
exponent = strlen(buf) - 1;
}
// routine for constructor use
v o i d set_self_ar( char *n1, char *n2, i n t os ) {
i n t l1 = strlen(n1), l2 = strlen(n2);
strcpy( longname, n1 );
longname[l1] = ’[’;
strcpy( longname+l1+1, n2 );
longname[l1+l2+1] = ’]’;
longname[l1+l2+2] = 0;
set_self( longname, os );
};
v o i d set_self_2( char *n1, char *n2, i n t os ) {
strcpy( longname, n1 );
strcat( longname, n2 );
set_self( longname, os );
};
v o i d set_self( char *n, i n t os ) {
name = n; offset = os;
in_world = FALSE; // Uli: variables are local by default
# i f d e f ALIGN
byteOffset = offset/8;
B.1 C++ Code 119

#endif
};
// assignment
double o p e r a t o r= (double val) { r e t u r n value(val); }
# i f n d e f NO_RUN_TIME_CHECKING
v o i d undef_error() c o n s t ;
i n l i n e o p e r a t o r double () c o n s t
{
i f (isundefined())
undef_error();
r e t u r n value();
};
#else
i n l i n e o p e r a t o r double () c o n s t { r e t u r n value(); };
#endif
// data access routines
i n l i n e v o i d clear() { value((double )0); initialized = TRUE; };
i n l i n e v o i d undefine() { undefined(); initialized = TRUE; };
i n l i n e v o i d reset() { undefined(); initialized = FALSE; };
// routine for setting undef_value
i n l i n e s t a t i c double set_undef_value(){
char buf[20];
double x,y;
x = 0;
sprintf(buf,"%+le",x);
buf[0] = ’-’;
sscanf(buf,"%le",&y);
r e t u r n y;
};
// routine for setting format_string_1 and format_string_2
v o i d set_formats_string(){
char buf[10];
cout.setf(ios::scientific);
cout.precision(8);
strcpy(format_string_1,"%+.");
strcpy(format_string_2,"%s:");
sprintf(buf,"%dle",accuracy - 1);
strcat(format_string_1,buf);
strcat(format_string_2,format_string_1);
};
// routine used to compact buf in order to store a double value in
// size_in_byte byte of the state vector
u n s i g n e d char* compact_buf(char* buf){
char aux_buf[100] = "\0";
u n s i g n e d char* zip_buf;
i n t diff_length;
u n s i g n e d s h o r t i,offset;
u n s i g n e d char c1,c2;
//initialed zip_buf
zip_buf = ( u n s i g n e d char*) malloc((size_in_byte+1)*
s i z e o f ( u n s i g n e d char));
i f (zip_buf == NULL)
Error.Error("Error in compact_buf: unable to allocate memory.");
// aux_buf contains buf without ’.’ and ’e’
strncpy(aux_buf,buf,2);
strncat(aux_buf,buf+3,accuracy-1);
// offset is used to handle the special case accuracy = 1
120 Chapter B. FHP-Murϕ: the Verifier

offset = accuracy > 1 ? 3 : 2;


strcat(aux_buf,buf+accuracy+offset);
i f ((diff_length = strlen(aux_buf) - 2*size_in_byte)>0)
// aux_buf needs more than ’size_in_byte’ byte;
// this case can occur when value’s exponent
// take up more than ’exponent’ byte;
// the only thing to do is to verify that
// whith a loss of accuracy value fits in state vector
{
// can’t have a number with accuracy less or equal to zero
i f ((diff_length - accuracy)>=0)
Error.Error(tsprintf("Error in compact_buf: unable to store %s\
in state vector",buf));
strcpy(buf,""); // reset buf
// copy aux_buf in buf with loss of accuracy
strncat(buf,aux_buf,accuracy-diff_length+1);
strcat(buf,aux_buf+accuracy+1);
strcpy(aux_buf,""); // reset aux_buf
strcpy(aux_buf,buf); // copy buf in aux_buf
}
/* compacting aux_buf. This is the code:
0 -> 0001 7 -> 1000
1 -> 0010 8 -> 1001
2 -> 0011 9 -> 1010
3 -> 0100 + -> 1011
4 -> 0101 - -> 1100
5 -> 0110 -> 1101 used when the aux_buf’s length is odd
6 -> 0111
Note: the code start with 0001 because ’\0’ is the null
character of a string */
i f ((strlen(aux_buf) % 2) != 0) // aux_buf’s length is odd
aux_buf[strlen(aux_buf)] = 13;
// the auxiliary code is put in the last position of aux_buf
// now, aux_buf’s length is even
aux_buf[strlen(aux_buf)] = ’\0’;
f o r (i=0;i<strlen(aux_buf);i+=2)
{
s w i t c h (aux_buf[i]) // setting c1
{
c a s e ’+’: {c1 = 11;break;}
c a s e ’-’: {c1 = 12;break;}
d e f a u l t : {c1 = aux_buf[i] - ’0’ + 1;}
}
s w i t c h (aux_buf[i+1]) // setting c2
{
c a s e ’+’: {c2 = 11;break;}
c a s e ’-’: {c2 = 12;break;}
c a s e 13: {c2 = 13;break;}
d e f a u l t : {c2 = aux_buf[i+1] - ’0’ + 1;}
}
// now, c1 and c2 will take up a byte in zipbuf
c1 = (c1 << 4) | c2;
zip_buf[i/2] = c1;
}
zip_buf[i/2] = ’\0’;
r e t u r n zip_buf;
};
// routine used to decompact buf in order to read a double value
// from the state vector
char* decompact_buf( u n s i g n e d char* buf) c o n s t {
u n s i g n e d char c;
B.1 C++ Code 121

char c1,c2;
u n s i g n e d s h o r t i,j;
char* unzip_buf;
char aux_buf[100];
//initialed unzip_buf
unzip_buf = (char*) malloc((accuracy+exponent+20)* s i z e o f (char));
i f (unzip_buf == NULL)
Error.Error("Error in decompact_buf: unable to allocate memory.");
j = 0;
f o r (i=0;i<size_in_byte;i++)
{
// decompacting in aux_buf
c = buf[i];
c1 = c >> 4;
c2 = c & 0xF;
s w i t c h (c1) // setting c1
{
c a s e 11: {c1 = ’+’;break;}
c a s e 12: {c1 = ’-’;break;}
d e f a u l t : {c1 = c1 + ’0’ - 1;}
}
s w i t c h (c2) // setting c2
{
c a s e 11: {c2 = ’+’;break;}
c a s e 12: {c2 = ’-’;break;}
c a s e 13: {c2 = 13;break;}
d e f a u l t : {c2 = c2 + ’0’ - 1;}
}
aux_buf[j++] = c1;
aux_buf[j++] = c2;
}
aux_buf[j] = ’\0’;
// now, we can build unzip_buf storing a double value
j = 0;
f o r (i=0;i<strlen(aux_buf);i++)
{
i f (i == 2)
unzip_buf[j++] = ’.’;
i f (((aux_buf[i] == ’+’) || (aux_buf[i] == ’-’)) && (i > 0))
unzip_buf[j++] = ’e’;
i f (aux_buf[i] != 13)
unzip_buf[j++] = aux_buf[i];
}
unzip_buf[j] = ’\0’;
r e t u r n unzip_buf;
};
# i f d e f ALIGN
// routine used to store a double value in the state vector when
// the states aren’t bit-compacted
v o i d setreal(double value){
char buf[100];
u n s i g n e d char *zip_buf;
u n s i g n e d s h o r t i;
sprintf(buf,format_string_1,value); // buf contains value
zip_buf = compact_buf(buf); // now, we can copy zip_buf in bits
f o r (i=0;i<size_in_byte;i++)
{
workingstate->bits[i+byteOffset] = zip_buf[i];
}
free(zip_buf);
};
#else
122 Chapter B. FHP-Murϕ: the Verifier

// routine used to modify the struct where of position type on


// the basis of offset’s value
i n l i n e v o i d set_where( i n t offset,position& where) c o n s t {
where.longoffset = offset / 32;
where.shift1 = offset % 32;
where.mask1 = (1<<8) - 1;
where.mask1 <<= where.shift1;
where.mask2 = 0;
i f ((offset+8)/32 != where.longoffset)
{
where.shift2 = 32-where.shift1;
where.mask2 = (1<<(8-where.shift2)) - 1;
}
where.longoffset *= 4;
};
// routine used to store a double value in the state vector when
// the states are bit-compacted (option -b)
v o i d setrealb (double value){
char buf[100];
u n s i g n e d char* zip_buf;
u n s i g n e d s h o r t i;
i n t os;
position where;
sprintf(buf,format_string_1,value); // buf contains value
zip_buf = compact_buf(buf); // now, we can copy zip_buf in bits
// in order to store zip_buf in bits when the states are bit-
// compacted we simply call set() size_in_byte times
os = offset;
f o r (i=0;i<size_in_byte;i++)
{
set_where(os,where);
workingstate->set(&where,zip_buf[i]);
os += 8;
}
free(zip_buf);
};
#endif
# i f d e f ALIGN
// routine used to read a double value from the state vector when
// the states aren’t bit-compacted
double getreal(state* st) c o n s t {
u n s i g n e d char buf[100];
char* unzip_buf;
double x;
u n s i g n e d s h o r t i;
f o r (i=0;i<size_in_byte;i++)
{
buf[i] = st->bits[i+byteOffset];
}
buf[i] = ’\0’;
unzip_buf = decompact_buf(buf);
sscanf(unzip_buf,"%le",&x);
free(unzip_buf);
r e t u r n x;
};
#else
// routine used to read a double value from the state vector when
// the states are bit-compacted (option -b)
double getrealb(state* st) c o n s t {
u n s i g n e d char buf[100];
char* unzip_buf;
double x;
B.1 C++ Code 123

u n s i g n e d s h o r t i;
i n t os;
position where;
// in order to read a double from bits when the states are bit-
// compacted we simply call get() size_in_byte times
os = offset;
f o r (i=0;i<size_in_byte;i++)
{
set_where(os,where);
buf[i] = st->get(&where);
os += 8;
}
buf[i] = ’\0’;
unzip_buf = decompact_buf(buf);
sscanf(unzip_buf,"%le",&x);
free(unzip_buf);
r e t u r n x;
};
#endif
i n l i n e c o n s t double value() c o n s t {
i f (in_world)
# i f d e f ALIGN
r e t u r n getreal(workingstate);
#else
r e t u r n getrealb(workingstate);
#endif
else
r e t u r n cvalue;
};
i n l i n e double value( double val){// fixed to manage exponent limits
char *buf, *tmp;
i n t ex_value;
buf = tsprintf(format_string_1,val);
i f (!(strcmp(buf,"+nan")) || !(strcmp(buf,"-nan")))
Error.Error("Error in value(val): probably either division by \
zero.");
i f (!(strcmp(buf,"+inf")) || !(strcmp(buf,"-inf")))
Error.Error("Error in value(val): exponent out of bounds.");
tmp = strstr(buf, "e");
sscanf(++tmp,"%d",&ex_value);//im: jump ’e’
i f (ex_value < -exponent_value+1)
val = ( double )0;
e l s e i f (ex_value > exponent_value)
Error.Error("Error in value(val): exponent %d out of bounds.",
ex_value);
i f (in_world)
# i f d e f ALIGN
setreal(val);
#else
setrealb(val);
#endif
else
{
// used only in this case, setreal(b) doesn’t need this
sscanf(buf,"%le",&val); // adjust precision of value
cvalue = val;
}
free(buf);
r e t u r n val;
};
i n l i n e v o i d undefined(){
defined(FALSE);
124 Chapter B. FHP-Murϕ: the Verifier

};
i n l i n e v o i d defined( b o o l val){
i f (!val)
i f (!in_world)
cvalue = undef_value;
else
# i f d e f ALIGN
setreal(undef_value);
#else
setrealb(undef_value);
#endif
};
i n l i n e b o o l defined() c o n s t {
double x;
char *buf1,*buf2;
b o o l isdefined;
i f (in_world)
# i f d e f ALIGN
x = getreal(workingstate);
#else
x = getrealb(workingstate);
#endif
else
x = cvalue;
buf1 = tsprintf("%+le",undef_value);
buf2 = tsprintf("%+le",x);
isdefined = strcmp(buf1,buf2);
free(buf1);
free(buf2);
r e t u r n (isdefined);
};
i n l i n e b o o l isundefined() c o n s t {
r e t u r n !(defined());
};
// printing, etc.
v i r t u a l v o i d print(){
char c;
i f (defined())
{
printf(format_string_2,name,value());
printf("\n");
}
else
cout << name << ":Undefined\n";
};
f r i e n d ostream& o p e r a t o r << (ostream& s, mu__real& val){
i f (val.defined()) s << (double ) val.value();
e l s e s << "Undefined" ;
r e t u r n s;
}
v o i d print_diff(state *prev);
// transfer routines
v o i d to_state(state *thestate){
// Uli: copy value (which is probably undefined)
double val = value();
in_world = TRUE;
value(val);
};
v o i d from_state(state *thestate) {};
B.1 C++ Code 125

// comparing routines, for symmetry


f r i e n d i n t CompareWeight(mu__real& a, mu__real& b)
{
i f (!a.defined() && !b.defined())
r e t u r n 0;
e l s e i f (!a.defined())
r e t u r n -1;
e l s e i f (!b.defined())
r e t u r n 1;
e l s e i f (a.value()==b.value()) r e t u r n 0;
e l s e i f (a.value()>b.value()) r e t u r n 1;
e l s e r e t u r n -1;
};
f r i e n d i n t Compare(mu__real& a, mu__real& b)
{
i f (!a.defined() && !b.defined())
r e t u r n 0;
e l s e i f (!a.defined())
r e t u r n -1;
e l s e i f (!b.defined())
r e t u r n 1;
e l s e i f (a.value()==b.value()) r e t u r n 0;
e l s e i f (a.value()>b.value()) r e t u r n 1;
e l s e r e t u r n -1;
};
v i r t u a l v o i d MultisetSort(){};
};
#endif

B.1.4 M ODIFICATIONS TO MU REAL .C

..
.
/* undef_value’s definition */
double mu__real::undef_value = mu__real::set_undef_value();

B.1.5 M ODIFICATIONS TO MU IO . H

..
.
/* Argclass inspired by Andreas\’ code. */
c l a s s argclass
{
i n t argc;
char **argv;
public:
// trace options
argbool print_trace;
argbool full_trace;
argbool trace_all;
argbool find_errors;
argnum max_errors;
argnum verbose_from_state;
argbool use_verbose_from_state;
argstr verbose_file;
126 Chapter B. FHP-Murϕ: the Verifier

// memory options
argnum mem;
// progress report options
argnum progress_count;
argbool print_progress;
// main algorithm options
argmain_alg main_alg;
// symmetry option
argbool symmetry_reduction;
argbool multiset_reduction;
argsym_alg sym_alg;
argnum perm_limit;
argbool debug_sym;
// Uli: hash compaction options
# i f d e f HASHC
argnum num_bits;
argbool trace_file;
#endif
// testing parameter
argnum test_parameter1;
argnum test_parameter2;
// miscelleneous
argnum loopmax;
argnum verbose;
argbool no_deadlock;
argbool print_options;
argbool print_license;
argbool print_rule;
argbool print_hash;
// cached murphi options
argnum max_collrate; // percent value: 90 -> 0.9
argnum max_level; //maximum bfs level
// pmurphik options
argbool swap_level;
argbool queue_compr;
argbool fpel;
// bpctl options
argbool state_compr, ctrl_next, disable_exit, safety;
// simulation
argbool simulate_prob;
// supporting routines
argclass( i n t ac, char** av);
˜argclass() {};
v o i d ProcessOptions(string_iterator *options);
b o o l Flag(char *arg);
v o i d PrintInfo( v o i d );
v o i d PrintOptions( v o i d );
v o i d PrintLicense( v o i d );
};
/****************************************
Printing functions.
****************************************/
c l a s s ReportManager
{
v o i d print_trace_aux(StatePtr p); // changed by Uli
public:
ReportManager();
v o i d CheckConsistentVersion();
B.1 C++ Code 127

v o i d StartSimulation();
void print_algorithm();
void print_warning();
void print_header( v o i d );
void print_trace_with_theworld();
void print_trace_with_curstate();
void print_progress( i n t level, b o o l forceprint = f a l s e ,
b o o l endlevel = f a l s e );
void print_no_error( i n t );
void print_summary( b o o l );
void print_curstate( v o i d );
void print_dfs_deadlock( v o i d );
void print_retrack( v o i d );
void print_fire_startstate();
void print_fire_rule();
void print_fire_rule(double );
void print_fire_rule_diff(state * s);
void print_fire_rule_diff(state * s, double );
void print_trace_all();
void print_verbose_header();
void print_hashtable();
void print_final_report( i n t = 0);
};

B.1.6 M ODIFICATIONS TO MU IO .C

..
.
// Pmurphik options
# d e f i n e QUEUE_COMPRESSION_FLAG "-queuecomp"
# d e f i n e SWAP_LEVEL_FLAG "-swaplevel"
# d e f i n e FORCE_PRINTING_END_LEVEL "-fpel"
// Bpctl verification options
# d e f i n e SAFETY_ALGO "-safety"
# d e f i n e COMPRESS_NEXT_FLAG "-comp_next"
# d e f i n e CTRL_NEXT_STATES_FLAG "-ctrl_next"
# d e f i n e VERBOSE_NUM "-verbose"
# d e f i n e DISABLE_EXIT "-calc_prob"
// simulation
# d e f i n e SIMULATE_PROB "-simulate_prob"

..
.
/****************************************
Implementation for the argclass class for handling runtime
arguments.
****************************************/
argclass::argclass( i n t ac, char** av)
: argc(ac), argv(av),
print_trace (FALSE, "trace printing"),
full_trace (FALSE, "printing diff/full states in trace"),
trace_all (FALSE, "printing all states"),
find_errors (FALSE, "continuing after error"),
max_errors (DEFAULT_MAX_ERRORS, "maximum number of errors"),
mem (DEFAULT_MEM, "memory allocation"),
progress_count (1000, "progress count"),
print_progress (TRUE, "progress printing"),
main_alg (argmain_alg::Verify_bfs, "main algorithm"),
128 Chapter B. FHP-Murϕ: the Verifier

loopmax (DEF_LOOPMAX,"maximium loop count"),


verbose (0, "verbose"),
verbose_from_state (0,"verbose controlled mode"),
verbose_file ("","verbose controlled mode to file"),
use_verbose_from_state (FALSE,"use verbose controlled mode"),
no_deadlock (FALSE, "deadlock detection"),
print_options (FALSE, "options printing"),
print_license (FALSE, "license printing"),
print_rule (FALSE, "rule information printing"),
print_hash (FALSE, "hashtable information printing"),
symmetry_reduction (TRUE, "symmetry option"),
sym_alg (argsym_alg::Heuristic_Small_Mem_Canonicalize,
"symmetry algorithm"),
perm_limit (10,"permutation limit"),
multiset_reduction (TRUE, "multiset option"),
test_parameter1 (100,"testing parameter1"),
test_parameter2 (100,"testing parameter2"),
# i f d e f HASHC
num_bits (DEFAULT_BITS, "stored bits"), // added by Uli
trace_file (FALSE, "trace info file"),
#endif
debug_sym (FALSE, "debug symmetry"),
max_collrate (90,"max collision rate"),
max_level (0,"max bfs level"),
swap_level (FALSE, "swap only at BFS level changes"),
simulate_prob (FALSE, "Make a simulation"),
fpel (FALSE, "force printing end level"),
queue_compr (FALSE, "compress the queue"),
ctrl_next (FALSE, "control the sum of outgoing probabiliti\
es to be 1.0"),
state_compr (FALSE, "compress the next states list"),
disable_exit (FALSE, "calculates the correct probability for \
the outer probability"),
safety (FALSE, "uses the BF algorithm for the safety ve\
rification")
{
string_iterator *temp = NULL;
temp = new arg_iterator(ac, av);
ProcessOptions( temp );
d e l e t e temp;
temp = NULL;
# i f d e f HASHC
// Uli: do not use trace info file in dfs case
i f (main_alg.mode == argmain_alg::Verify_dfs) {
i f (trace_file.value) {
d e l e t e TraceFile;
trace_file.reset(FALSE);
}
}
// Uli: check if trace is wanted but cannot be generated
i f (main_alg.mode == argmain_alg::Verify_bfs)
i f (print_trace.value && !trace_file.value)
Error.Notrace
("Cannot print error trace if you do not specify trace inf\
o file.");
// Uli: set number of bytes in trace info file
// cannot be done earlier since number of bits may be unknown
i f (trace_file.value) {
TraceFile->setBytes( i n t (num_bits.value));
print_trace.reset(TRUE);
}
#endif
// avoid mixing verbose and progress report
B.1 C++ Code 129

i f (verbose.value) print_progress.set(FALSE);
i f (main_alg.mode != argmain_alg::Verify_bfs && find_errors.value)
// changed by Uli
{
Error.Notrace("Please use -vbfs for finding multiple errors \
in single run.");
}
i f (sym_alg.mode != argsym_alg::Heuristic_Small_Mem_Canonicalize
&& perm_limit.value !=0)
{
perm_limit.set(0);
}
i f (debug_sym.value) symmetry_reduction.reset(FALSE);
PrintInfo();
}

..
.
v o i d argclass::ProcessOptions(string_iterator *options)
{
char* option;
b o o l no_verification = FALSE;
u n s i g n e d l o n g temp;
char temp_str[256];
f o r ( options->start(); !options->done(); options-> n e x t () )
{
option = options->value();
// cached murphi options begin
i f ( strncmp(option, MAX_COLLRATE_PREFIX,
strlen(MAX_COLLRATE_PREFIX) ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
continue ;
};
i f ( strncmp(option, MAX_LEVEL_PREFIX,
strlen(MAX_LEVEL_PREFIX) ) == 0 )
{
i f ( strlen(option) <= strlen(MAX_LEVEL_PREFIX) )
/* We cannot have a space before the number */
{
sscanf( options->nextvalue(), "%s", temp_str );
i f (isdigit(temp_str[0]))
{
sscanf( temp_str, "%u", (u n s i g n e d l o n g ) &temp );
options-> n e x t ();
}
else
Error.Notrace("Unrecognized bound for BPMC. Do ’%s -h’ f\
or list of valid arguments.", argv[0]);
}
else
{
sscanf( options->value() + strlen(MAX_LEVEL_PREFIX), "%s",
temp_str);
i f (isdigit(temp_str[0]))
sscanf( temp_str, "%u", (u n s i g n e d l o n g ) &temp );
else
Error.Notrace("Unrecognized bound for BPMC. Do ’%s -h’ for li\
st of valid arguments.", argv[0]);
130 Chapter B. FHP-Murϕ: the Verifier

}
i f (temp < 0)
Error.Notrace("Invalid bound for BPMC. Valid values must be po\
sitive or zero");
max_level.set(temp);
continue ;
};
// cached murphi options end
// pmurphik options begin
i f ( strcmp(option, QUEUE_COMPRESSION_FLAG) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
continue ;
};
i f ( strcmp(option, SWAP_LEVEL_FLAG) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
continue ;
};
i f ( strcmp(option, FORCE_PRINTING_END_LEVEL) == 0 )
{
fpel.set( t r u e );
continue ;
};
// pmurphik options end
// bpctl options begin
i f ( strcmp(option, SAFETY_ALGO) == 0 )
{
safety.set( t r u e );
continue ;
};
i f ( strcmp(option, CTRL_NEXT_STATES_FLAG) == 0 )
{
ctrl_next.set( t r u e );
continue ;
};
i f ( strcmp(option, COMPRESS_NEXT_FLAG) == 0 )
{
state_compr.set( t r u e );
continue ;
};
i f ( strcmp(option, DISABLE_EXIT) == 0 )
{
disable_exit.set( t r u e );
continue ;
};
i f ( strcmp( option, VERBOSE_NUM ) == 0 )
{
i f ( strlen(option) <= strlen(VERBOSE_NUM) )
/* We cannot have a space before the number */
{
sscanf( options->nextvalue(), "%s", temp_str );
i f (isdigit(temp_str[0]))
{
sscanf( temp_str, "%u", ( u n s i g n e d l o n g ) &temp );
options-> n e x t ();
}
else
B.1 C++ Code 131

Error.Notrace("Unrecognized verbose value. Do ’%s -h’ for \


list of valid arguments.", argv[0]);
}
else
{
sscanf( options->value() + strlen(VERBOSE_NUM), "%s",
temp_str );
i f (isdigit(temp_str[0]))
sscanf( temp_str, "%u", (u n s i g n e d l o n g ) &temp );
else
Error.Notrace("Unrecognized verbose value. Do ’%s -h’ for \
list of valid arguments.", argv[0]);
}
verbose.set(temp);
continue ;
}
i f ( strcmp( option, PRINT_10_FLAG ) == 0 )
{
progress_count.set(10);
print_progress.set(TRUE);
continue ;
}
i f ( strcmp( option, PRINT_100_FLAG ) == 0 )
{
progress_count.set(100);
print_progress.set(TRUE);
continue ;
}
i f ( strcmp( option, PRINT_1000_FLAG ) == 0 )
{
progress_count.set(1000);
print_progress.set(TRUE);
continue ;
}
i f ( strcmp( option, PRINT_10000_FLAG ) == 0 )
{
progress_count.set(10000);
print_progress.set(TRUE);
continue ;
}
i f ( strcmp( option, PRINT_100000_FLAG ) == 0 )
{
progress_count.set(100000);
print_progress.set(TRUE);
continue ;
}
i f ( strcmp( option, PRINT_NONE_FLAG ) == 0 )
{
print_progress.set(FALSE);
continue ;
}
// bpctl options end
// simulation
i f ( strcmp(option, SIMULATE_PROB) == 0 )
{
simulate_prob.set(TRUE);
continue ;
};
/* we have to handle memory as a special case. */
i f ( strncmp(option, MEM_MEG_PREFIX, 2 ) == 0 )
{
i f ( strlen(option) <= strlen(MEM_MEG_PREFIX) )
/* We cannot have a space before the number */
{
132 Chapter B. FHP-Murϕ: the Verifier

sscanf( options->nextvalue(), "%s", temp_str );


i f (isdigit(temp_str[0]))
{
sscanf( temp_str, "%u", ( u n s i g n e d l o n g ) &temp );
options-> n e x t ();
}
else
Error.Notrace("Unrecognized memory size. Do ’%s -h’ for li\
st of valid arguments.", argv[0]);
}
else
{
sscanf( options->value() + strlen(MEM_MEG_PREFIX), "%s",
temp_str );
i f (isdigit(temp_str[0]))
sscanf( temp_str, "%u", ( u n s i g n e d l o n g ) &temp );
else
Error.Notrace("Unrecognized memory size. Do ’%s -h’ for li\
st of valid arguments.", argv[0]);
}
mem.set(temp * 0x100000L); /* times 1 Meg. */
continue ;
};
i f ( strncmp(option, MEM_K_PREFIX, strlen(MEM_K_PREFIX) ) == 0 )
{
i f ( strlen(option) <= strlen(MEM_K_PREFIX) )
/* We cannot have a space before the number */
{
sscanf( options->nextvalue(), "%s", temp_str );
i f (isdigit(temp_str[0]))
{
sscanf( temp_str, "%u", ( u n s i g n e d l o n g ) &temp );
options-> n e x t ();
}
else
Error.Notrace("Unrecognized memory size. Do ’%s -h’ for li\
st of valid arguments.", argv[0]);
}
else
{
sscanf( options->value() + strlen(MEM_K_PREFIX), "%s",
temp_str );
i f (isdigit(temp_str[0]))
sscanf( temp_str, "%u", ( u n s i g n e d l o n g ) &temp );
else
Error.Notrace("Unrecognized memory size. Do ’%s -h’ for li\
st of valid arguments.", argv[0]);
}
mem.set(temp * 0x400L); /* times 1 Kilobyte. */
continue ;
};
# i f d e f HASHC // never defined (disabled by --bpctl)
// added by Uli
i f ( strncmp(option, NUM_BITS_PREFIX,
strlen(NUM_BITS_PREFIX) ) == 0 )
{
i f ( strlen(option) <= strlen(NUM_BITS_PREFIX) )
// there is a space before the number
{
sscanf( options->nextvalue(), "%s", temp_str );
i f (isdigit(temp_str[0]))
{
sscanf( temp_str, "%u", ( u n s i g n e d l o n g ) &temp );
options-> n e x t ();
}
else
B.1 C++ Code 133

Error.Notrace("Unrecognized number of bits.", argv[0]);


}
e l s e // no space
{
sscanf( options->value() + strlen(NUM_BITS_PREFIX), "%s",
temp_str );
i f (isdigit(temp_str[0]))
sscanf( temp_str, "%u", (u n s i g n e d l o n g ) &temp );
else
Error.Notrace("Unrecognized number of bits.", argv[0]);
}
i f (temp>64 || temp<1)
Error.Notrace("Number of bits not allowed.");
num_bits.set(temp);
continue ;
};
// added by Uli
i f ( strncmp(option, TRACE_DIR_PREFIX,
strlen(TRACE_DIR_PREFIX) ) == 0 )
{
i f ( strlen(option) <= strlen(TRACE_DIR_PREFIX) )
// there is a space before the filename
{
sscanf( options->nextvalue(), "%s", temp_str );
options-> n e x t ();
}
e l s e // no space
{
sscanf( options->value() + strlen(NUM_BITS_PREFIX), "%s",
temp_str);
}
TraceFile = new TraceFileManager(temp_str);
trace_file.set(TRUE);
continue ;
};
#endif
i f ( strncmp(option, LOOPMAX_PREFIX,
strlen(LOOPMAX_PREFIX) ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
};
i f ( strncmp(option, PERM_LIMIT, strlen(PERM_LIMIT) ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
};
i f ( strncmp(option, TEST1_PREFIX, strlen(TEST1_PREFIX) ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
};
i f ( strncmp(option, TEST2_PREFIX, strlen(TEST2_PREFIX) ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
};
i f ( strcmp( option, SIMULATE_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, VER ifY_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, VER ifY_BFS_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, VER ifY_DFS_FLAG ) == 0 )
134 Chapter B. FHP-Murϕ: the Verifier

{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, NO_DEADLOCK_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, CONTINUE_AFTER_ERROR_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strncmp( option, MAX_NUM_ERRORS_PREFIX,
strlen(MAX_NUM_ERRORS_PREFIX) ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
};
/* control frequency of printouts. */
i f ( strcmp( option, VERBOSE_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp ( option, VERBOSE_FROM ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp ( option, VERBOSE_FILE ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
/* handle trace types. */
i f ( strcmp( option, TRACE_VIOLATE_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, TRACE_D ifF_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, TRACE_FULL_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, TRACE_ALL_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, TRACE_NONE_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, HELP_FLAG ) == 0 )
{
print_options.set(TRUE);
no_ver ification = TRUE;
continue ;
}
i f ( strcmp( option, LICENSE_FLAG ) == 0 )
{
print_license.set(TRUE);
continue ;
}
i f ( strcmp( option, PRINT_RULE_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strcmp( option, NO_SYM_FLAG ) == 0 )
{
B.1 C++ Code 135

Error.Notrace("Option %s not available in BPMC", option);


}
i f ( strcmp( option, NO_MULTISET_FLAG ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
}
i f ( strncmp(option, SYMMETRY_PREFIX,
strlen(SYMMETRY_PREFIX) ) == 0 )
{
Error.Notrace("Option %s not available in BPMC", option);
};
Error.Notrace("Unrecognized flag %s. Do ’%s -h’ for list o\
f valid arguments.", option, argv[0]);
continue ;
}
i f (fpel.value && !safety.value)
Error.Notrace("-fpel available only with -safety");
i f (no_verification)
main_alg.set(argmain_alg::Nothing);
}
v o i d argclass::PrintOptions( v o i d ) // changes by Uli
{
cout << "Options:\n"
<< "1) General:\n"
<< "\t-h help.\n"
<< "\t-l print license.\n"
<< "2) Verification Strategy\n"
<< "\t-safety use bfs (safety properties only)\n"
<< "\t-simulate_prob simulation\n"
<< "3) Others Options: (default: -m8, -p3, -loop1000)\n"
<< "\t-m<n> amount of memory for closed hash table in Mb.\
\n"
<< "\t-k<n> same, but in Kb.\n"
<< "\t-p make verification verbose.\n"
<< "\t-pfrom<n> make verification (via bfs) verbose from stat\
e n.\n"
<< "\t-p<n> report progress every 10ˆn events, n in 1..5.\
\n"
<< "\t-pn print no progress reports.\n"
<< "\t-fpel force printing at end of level (only with -sa\
fety)"
<< "\t-pr print out rule information.\n"
<< "\t-verbose<n> print infos on the algorithm execution.\n"
<< "\t-calc_prob<n> computes the exact probability for the outer \
BPCTL formula.\n"
<< "4) Error Trace Handling: disabled\n"
<< "5) Reduction Technique: disabled\n"
<< "\n";
}

..
.
/************************************************************/
v o i d ReportManager::print_header( v o i d ) // changes by Uli
{
cout << "\n====================================="
<< "=====================================\n"
<< MURPHI_VERSION << "\n"
<< "Finite-state Concurrent System Verifier.\n"
<< "\n"
<< MURPHI_VERSION << " is based on Murphi release 3.1.\n"
<< MURPHI_VERSION << " :\n"
<< "Copyright (C) 2001 by E. Tronci, G. Della Penna, I. Mel\
136 Chapter B. FHP-Murϕ: the Verifier

atti, B. Intrigila, M. Zilli.\n"


<< "Murphi release 3.1 :\n"
<< "Copyright (C) 1992 - 1999 by the Board of Trustees of\n"
<< "Leland Stanford Junior University.\n"
<< "\n====================================="
<< "=====================================\n"
<< "\nProtocol: " << PROTOCOL_NAME << "\n";
// cout.flush();
// flushing cout had seemed to cause some weirdnesses.
}

..
.
v o i d ReportManager::print_progress( i n t level, b o o l forceprint,
b o o l endlevel )
{
s t a t i c b o o l initialized = FALSE;
// pring progress report every <args->progress_count> new states
// found
i f ((forceprint) || ( args->print_progress.value
&& StateSet->NumElts() % args->progress_count.value == 0 )) {
i f (!initialized) {
cout << "\nProgress Report:\n\n";
initialized = TRUE;
}
i f (args->safety.value && endlevel) {
cout << "\n***************\n";
cout << "Level " << StateSet->GetCurrentLevel() - 1 << " comple\
ted.\n\n";
} else {
cout << "\t";
cout << StateSet->NumElts() << " states explored in "
<< SecondsSinceStart() << "s, with "
<< Rules->NumRulesFired() << " rules fired.";
}
i f (args->safety.value && !endlevel)
cout << " Exploring level " << StateSet->GetCurrentLevel() << "."
<< " Formula probability "
<< setprecision(6) << setiosflags(ios::scientific)
<< E_p
<< setprecision(3) << resetiosflags(ios::scientific)
<< ".\n";
e l s e i f (!args->safety.value) {
cout << "Exploring level " << level << ".";
i f (args->verbose.value > 3)
cout << "\t\teffectiveness: "
<< StateSet->subf_cache->effectiveness() << "\n";
}
cout.flush();
}
}
/************************************************************/
v o i d ReportManager::print_no_error( i n t reason )
{
cout << "\n====================================="
<< "=====================================\n"
<< "\nResults:\n";
i f (reason==0)
cout << "\n\tExploration complete.";
e l s e i f (reason==3)
cout << "\n\tPCTL formula satisfied.";
e l s e i f (reason==4)
cout << "\n\tPCTL formula not satisfied.";
B.1 C++ Code 137

e l s e i f (reason==5)
cout << "\n\tPCTL formula satisfied (max depth not reached).";
e l s e i f (reason==6)
cout << "\n\tPCTL formula not satisfied (max depth not reached).";
}
/************************************************************/
v o i d ReportManager::print_summary( b o o l prob)
{
b o o l exist = FALSE;
i f (StateSet == NULL)
{
i f (args->max_level.value == 0)
cout << "\n\tVerification horizon set to 0";
else
cout << "\n\tVerification of an atomic proposition";
cout << ": no state space exploration perfomed.\n";
}
else
cout << "\nState Space Explored:\n\n"
<< "\t"
<< StateSet->NumElts() << " states, "
<< Rules->NumRulesFired() << " rules fired in "
<< SecondsSinceStart() << "s.\n"
<< "\tLevels Explored: " << StateSet->GetCurrentLevel()-1
<< "\n\n";
i f (prob) {
# i f d e f HASHC
// Uli: print omission probabilities
StateSet->PrintProb();
#endif
}
Rules->print_rules_information();
theworld.print_statistic();
}

..
.

/************************************************************/
v o i d ReportManager::print_fire_startstate()
{
cout << "Firing startstate "
<< StartState->LastStateName()
<< " with probability "
<< StartState->LastStateProb()
<< "\n"
<< "Obtained state:\n";
theworld.print();
cout << ’\n’;
}
/************************************************************/
v o i d ReportManager::print_fire_rule(double prob)
{
cout << "Firing rule "
<< Rules->LastRuleName()
<< " with probability "
<< prob
<< ’\n’
<< "Obtained state:\n";
theworld.print();
cout << ’\n’;
}
138 Chapter B. FHP-Murϕ: the Verifier

/************************************************************/
v o i d ReportManager::print_fire_rule()
{
cout << "Firing rule "
<< Rules->LastRuleName()
<< " with probability "
<< Rules->LastRuleProb()
<< ’\n’
<< "Obtained state:\n";
theworld.print();
cout << ’\n’;
}
/************************************************************/
v o i d ReportManager::print_fire_rule_diff(state * s, double prob)
{
cout << "Firing rule "
<< Rules->LastRuleName()
<< " with probability "
<< prob
<< ’\n’
<< "Obtained state:\n";
theworld.print_diff(s);
cout << ’\n’;
}
/************************************************************/
v o i d ReportManager::print_fire_rule_diff(state * s)
{
cout << "Firing rule "
<< Rules->LastRuleName()
<< " with probability "
<< Rules->LastRuleProb()
<< ’\n’
<< "Obtained state:\n";
theworld.print_diff(s);
cout << ’\n’;
}

B.1.7 M ODIFICATIONS TO MU UTIL DEP. H

..
.
c l a s s setofrules
{
protected:
BIT_BLOCK bits[BLOCKS_IN_SETOFRULES];
// to manage rules probabilities
double probabilities[RULES_IN_WORLD];
u n s i g n e d NumRules; // Uli: unsigned short -> unsigned
i n t Index( i n t i) c o n s t { r e t u r n i / BITS( BIT_BLOCK ); };
i n t Shift( i n t i) c o n s t { r e t u r n i % BITS( BIT_BLOCK ); };
i n t Get1( i n t i) c o n s t
{ r e t u r n ( bits[ Index(i) ] >> Shift(i) ) & 1; };
v o i d Set1( i n t i, i n t val) /* Set bit i to the low bit of val. */
{
i f ( (val & 1) != 0 )
bits[ Index(i) ] |= ( 1 << Shift(i));
else
bits [ Index(i) ] &= ˜( 1 << Shift(i));
};
B.1 C++ Code 139

public:
// set of rules manipulation
f r i e n d setofrules interset(setofrules rs1, setofrules rs2);
f r i e n d setofrules different(setofrules rs1, setofrules rs2);
f r i e n d b o o l subset(setofrules rs1, setofrules rs2);
// conflict set manipulation
f r i e n d setofrules conflict( u n s i g n e d rule);
setofrules()
: NumRules(0)
{ f o r ( i n t i=0; i<BLOCKS_IN_SETOFRULES; i++) bits[i]=0;};
v i r t u a l ˜setofrules() {};
b o o l in( i n t rule) c o n s t { r e t u r n ( b o o l ) Get1(rule); };
v o i d add( i n t rule, double prob = (double )1)
{
i f (!in(rule))
{
Set1(rule,TRUE);
NumRules++;
}
probabilities[rule] = prob; // added probability
};
v o i d remove( i n t rule)
{
i f (in(rule))
{
Set1(rule,FALSE);
NumRules--;
}
}
b o o l nonempty()
{
r e t u r n (NumRules!=0);
};
i n t size()
{
r e t u r n NumRules;
};
v o i d print()
{
cout << "The set of rules =\t";
f o r ( i n t i=0; i<RULES_IN_WORLD; i++)
cout << (Get1(i)?1:0) << ’,’;
cout << "\n";
};
// for simulation
v o i d removeall()
{
f o r ( i n t i=0; i<RULES_IN_WORLD; i++)
Set1(i,FALSE);
NumRules = 0;
};
// for simulation
v o i d includeall()
{
f o r ( i n t i=0; i<RULES_IN_WORLD; i++)
Set1(i,TRUE);
NumRules = RULES_IN_WORLD;
};
u n s i g n e d getnthrule( u n s i g n e d rule)
{
u n s i g n e d r=0;
i n t i=0;
140 Chapter B. FHP-Murϕ: the Verifier

w h i l e (1)
i f (Get1(i) && r==rule)
{ Set1(i,FALSE); NumRules--; r e t u r n ( u n s i g n e d) i; }
e l s e i f (Get1(i))
{ i++; r++; }
else
{ i++; }
};
// added prob
double prob( i n t rule)
{
r e t u r n probabilities[rule];
}
};

B.1.8 M ODIFICATIONS TO MU UTIL .C

..
.

unsigned long
NumStatesGivenBytes( u n s i g n e d l o n g bytes )
{
return (unsigned long)
((double )bytes*gPercentActiveStates
/((state_probability_queue::BytesForOneState()) +
state_probability_queue::BytesForOneState_hashtable()
));
}

B.1.9 M ODIFICATIONS TO MU VERIFIER . H

..
.

/* type definitions for PCTL formulas */


enum pctl_type {UNTIL_PCTL, NEXT_PCTL, AP_PCTL, AND_PCTL, OR_PCTL,
IMPL_PCTL, NOT_PCTL};
enum pctl_ord {PCTL_L, PCTL_LEQ, PCTL_G, PCTL_GEQ};
s t r u c t pctlformrec { // record for PCTL formula
pctl_type type;
boolfunc condition;
pctl_ord ord;
double prob_bound;
u n s i g n e d until_bound;
c o n s t s t r u c t pctlformrec *subf1, *subf2;
};

B.1.10 M ODIFICATIONS TO MU VERIFIER .C

..
.
B.1 C++ Code 141

i n t main( i n t argc, char **argv)


{
args = new argclass(argc, argv);
Algorithm = new AlgorithmManager();
i f (!args->print_options.value) {
i f (!args->simulate_prob.value) {
i f (args->safety.value) {
i f (!Algorithm->IsSafety()) {
cerr << "-safety is usable only with a finite horizon safety \
property" << endl;
exit(1);
}
Algorithm->verify_bfs();
}
else
Algorithm->verify_bpmc();
}
else
Algorithm->simulate();
}
cout.flush();
d e l e t e Algorithm; // fix: begin destruction chain
exit(0);
}

B.1.11 M ODIFICATIONS TO MU STATE . H

..
.
c l a s s aux_cache
{
randomGen random; // random number generator for random walk
// data
u n s i g n e d l o n g table_size; /* max size of the hash table */
state_prob_hor_form *table;/* pointer to the hash table */
dynBitVec *Full; /* whether element table[i] is used. */
u n s i g n e d l o n g num_elts; /* number of elements in table */
u n s i g n e d l o n g num_collisions; /* number of collisions in hashing */
u n s i g n e d l o n g ht_overwrite; /* overwrites in caching. gdp */
u n s i g n e d max_ht_chain_length;
u n s i g n e d l o n g num_called, num_failed;
// internal routines
/* check if element table[i] is empty */
b o o l is_empty( u n s i g n e d l o n g i )
{ r e t u r n Full->get(i) == 0; };
b o o l try_to_evaluate(state_prob_hor_form *, unsigned , b o o l &);
public:
// constructor
aux_cache( u n s i g n e d l o n g memory, u n s i g n e d max_ht_chain_length);
// destructor
v i r t u a l ˜aux_cache();
double simple_was_present(state_prob_hor_form * in);
b o o l insert(state_prob_hor_form * in);
b o o l try_to_evaluate(state_prob_hor_form * in, b o o l & val);
i n l i n e double effectiveness()
{ r e t u r n (double )num_failed/num_called; };
};
142 Chapter B. FHP-Murϕ: the Verifier

B.1.12 M ODIFICATIONS TO MU STATE .C

..
.
aux_cache::aux_cache( u n s i g n e d l o n g memory_bytes,
u n s i g n e d max_ht_chain_length)
: num_elts(0), num_collisions(0), ht_overwrite(0),
max_ht_chain_length(max_ht_chain_length), num_called(0),
num_failed(0)
{
table_size = NextPrime((u n s i g n e d l o n g )
(memory_bytes / s i z e o f (state_prob_hor_form)));
table = new state_prob_hor_form [table_size];
Full = new dynBitVec ( table_size );
}
aux_cache::˜aux_cache()
{
d e l e t e [] table; // only works for newer g++ versions
d e l e t e Full;
}
bool
aux_cache::try_to_evaluate(state_prob_hor_form * in, u n s i g n e d h,
b o o l & result)
{
i f (table[h]._horizon < in->_horizon &&
table[h]._probability > in->_subformula->prob_bound)
{
i f (in->_subformula->ord == PCTL_G)
{
result = TRUE;
r e t u r n TRUE;
}
e l s e i f (in->_subformula->ord == PCTL_LEQ)
{
result = FALSE;
r e t u r n TRUE;
}
}
e l s e i f (table[h]._horizon < in->_horizon &&
table[h]._probability >= in->_subformula->prob_bound)
{
i f (in->_subformula->ord == PCTL_GEQ)
{
result = TRUE;
r e t u r n TRUE;
}
e l s e i f (in->_subformula->ord == PCTL_L)
{
result = FALSE;
r e t u r n TRUE;
}
}
e l s e i f (table[h]._horizon > in->_horizon &&
table[h]._probability < in->_subformula->prob_bound)
{
i f (in->_subformula->ord == PCTL_L)
{
result = TRUE;
r e t u r n TRUE;
}
e l s e i f (in->_subformula->ord == PCTL_GEQ)
{
result = FALSE;
r e t u r n TRUE;
}
B.1 C++ Code 143

}
e l s e i f (table[h]._horizon > in->_horizon &&
table[h]._probability <= in->_subformula->prob_bound)
{
i f (in->_subformula->ord == PCTL_LEQ)
{
result = TRUE;
r e t u r n TRUE;
}
e l s e i f (in->_subformula->ord == PCTL_G)
{
result = FALSE;
r e t u r n TRUE;
}
}
r e t u r n FALSE;
}
bool
aux_cache::try_to_evaluate(state_prob_hor_form * in, b o o l & result)
{
u n s i g n e d l o n g key = in->_state.hashkey();
u n s i g n e d l o n g h1 = key % table_size;
u n s i g n e d l o n g h2 = 1 + key % ( table_size - 1 );
u n s i g n e d l o n g h = h1;
u n s i g n e d l o n g probe = 0;
// no hash compaction, uses double hashing
b o o l empty, equal= FALSE;
w h i l e (!(empty = is_empty(h)) &&
!(equal = in->compare_st_form(&table[h])) &&
(probe < max_ht_chain_length))
{
h = (h1 + probe * h2) % table_size; // double hashing
probe++;
}
w h i l e (!empty && equal && table[h]._horizon != in->_horizon)
{
i f (try_to_evaluate(in, h, result))
r e t u r n result;
h = (h1 + probe * h2) % table_size; // double hashing
probe++;
i f (!(empty = is_empty(h)))
equal = in->compare_st_form(&table[h]);
}
i f (!empty && equal && table[h]._horizon == in->_horizon)
{
s w i t c h (in->_subformula->ord)
{
c a s e PCTL_L :
result = table[h]._probability < in->_subformula->prob_bound;
break;
c a s e PCTL_LEQ :
result = table[h]._probability <= in->_subformula->prob_bound;
break;
c a s e PCTL_G :
result = table[h]._probability > in->_subformula->prob_bound;
break;
c a s e PCTL_GEQ :
result = table[h]._probability >= in->_subformula->prob_bound;
break;
default :
Error.Notrace("Internal error: invalid value %d for in->_s\
ubformula->ord", in->_subformula->ord);
r e t u r n FALSE;
}
144 Chapter B. FHP-Murϕ: the Verifier

r e t u r n TRUE;
}
else
r e t u r n FALSE;
};
double
aux_cache::simple_was_present(state_prob_hor_form * in)
{
u n s i g n e d l o n g key = in->_state.hashkey();
u n s i g n e d l o n g h1 = key % table_size;
u n s i g n e d l o n g h2 = 1 + key % ( table_size - 1 );
u n s i g n e d l o n g h = h1;
u n s i g n e d l o n g probe = 0;
// no hash compaction, uses double hashing
b o o l empty, equal= FALSE;
w h i l e (!(empty = is_empty(h)) &&
!(equal = in->compare_no_prob(&table[h])) &&
(probe < max_ht_chain_length))
{
h = (h1 + probe * h2) % table_size; // double hashing
num_collisions++;
probe++;
}
i f (!empty && equal && table[h]._horizon == in->_horizon)
r e t u r n table[h]._probability;
else
r e t u r n -1.0;
};
b o o l aux_cache::insert(state_prob_hor_form * in)
{
u n s i g n e d l o n g key = in->_state.hashkey();
u n s i g n e d l o n g h1 = key % table_size;
u n s i g n e d l o n g h2 = 1 + key % ( table_size - 1 );
u n s i g n e d l o n g h = h1;
u n s i g n e d l o n g probe = 0;
// no hash compaction, uses double hashing
b o o l empty, equal= FALSE;
c o n s t f l o a t param = 0.05;
num_called++;
w h i l e (!(empty = is_empty(h)) &&
!(equal = in->compare_no_prob(&table[h])) &&
(probe < max_ht_chain_length))
{
h = (h1 + probe * h2) % table_size; // double hashing
num_collisions++;
probe++;
}
i f (probe == max_ht_chain_length)
{
num_failed++;
i f (args->verbose.value > 3)
printf("num_failed:%u num_called:%u NumStates:%lu\n",
num_failed, num_called, StateSet->NumElts());
}
i f (num_failed >= param*num_called)
{
max_ht_chain_length *= 2;
i f (args->verbose.value > 3)
B.1 C++ Code 145

printf("\n\n\nmax_ht_changed from %u to %u\n\n\n\n",


max_ht_chain_length/2, max_ht_chain_length);
i f (max_ht_chain_length >= table_size/2)
r e t u r n FALSE;
num_failed = num_called = 0;
}
i f (empty) /* Go ahead and insert the element. */
{
table[h] = *in;
Full->set(h);
num_elts++;
r e t u r n TRUE;
}
e l s e i f (in->compare_st_form(&table[h]) &&
table[h]._horizon < in->_horizon)
{
table[h] = *in;
r e t u r n TRUE;
}
r e t u r n TRUE;
};

B.1.13 A DDED FILE SPLIT F ILE . H

This was written by Giuseppe Della Penna.

..
.

# i f n d e f _SPLITFILE_
# d e f i n e _SPLITFILE_
# d e f i n e DEFAULTDIR 0
# d e f i n e FORWARD 1
# d e f i n e BACKWARD 2
c l a s s splitFile {
public:
splitFile( u n s i g n e d l o n g splitsize, b o o l permanent = t r u e );
˜splitFile();
b o o l open(char *filename, char* access);
v o i d close();
u n s i g n e d l o n g read( v o i d * buffer, u n s i g n e d l o n g size,
u n s i g n e d l o n g count);
u n s i g n e d l o n g write( v o i d * buffer, u n s i g n e d l o n g size,
u n s i g n e d l o n g count);
u n s i g n e d l o n g tell(u n s i g n e d l o n g size=1);
v o i d seek( u n s i g n e d l o n g size, u n s i g n e d l o n g count, i n t from,
i n t direction=DEFAULTDIR);
v o i d seek( u n s i g n e d l o n g count, i n t from);
b o o l eof();
char *getBaseFileName() { r e t u r n _filename;}
v o i d setPermanent( b o o l perm) {_permanent = perm;}
protected:
v o i d CreateNextPart();
b o o l OpenNextPart();
b o o l GotoNextPart( b o o l write_mode);
i n l i n e u n s i g n e d l o n g curfilepos();
i n l i n e u n s i g n e d l o n g getpartsize( i n t npart);
u n s i g n e d l o n g writebytes( v o i d * buffer, u n s i g n e d l o n g size);
u n s i g n e d l o n g readbytes( v o i d * buffer, u n s i g n e d l o n g size);
146 Chapter B. FHP-Murϕ: the Verifier

b o o l seekbytes( u n s i g n e d l o n g size, i n t direction, i n t & part,


u n s i g n e d l o n g & skip);
private:
char _access[10];
char _filename[1020];
b o o l _permanent;
FILE *parts[100];
u n s i g n e d l o n g partsize[100];
i n t maxpart, curpart;
u n s i g n e d l o n g _splitsize;
b o o l _eof;
/* check for type size invariants only once */
s t a t i c b o o l sizechecks;
};
#endif

B.1.14 A DDED FILE SPLIT F ILE .C

This was written by Giuseppe Della Penna.

..
.
# d e f i n e min(a,b) (((a)<(b))?(a):(b))
# d e f i n e max(a,b) (((a)>(b))?(a):(b))
//Max # of parts handled by the splitFile class
//Adjust to match your needs
# d e f i n e MAX_PARTS 200
# i n c l u d e "splitFile.h"
b o o l splitFile::sizechecks = f a l s e ;
/*
* Creates (but not opens) a spliFile.
* The file will be subdivided in several files,
* each with a length <= slitsize.
* If permanent is false, the split file will be
* deleted when closed.
*
*/
splitFile::splitFile( u n s i g n e d l o n g splitsize, b o o l permanent)
: _splitsize(splitsize), maxpart(-1), curpart(-1), _eof( t r u e ),
_permanent(permanent)
{
i f (sizechecks == f a l s e ) {
i f ( s i z e o f (char) != 1) {
fprintf(stderr,"splitFile: error: sizeof(char) != 8");
exit(10);
}
i f ( s i z e o f (u n s i g n e d l o n g ) > s i z e o f (size_t)) {
fprintf(stderr,"splitFile: waning: size_t type is smaller than \
unsigned long!");
}
sizechecks= t r u e ;
}
memset(parts,0, s i z e o f (FILE*)*MAX_PARTS);
}
B.1 C++ Code 147

splitFile::˜splitFile()
{
close();
}
v o i d splitFile::CreateNextPart()
{
char filename[1024];
sprintf(filename,"%s.%02d",_filename,maxpart+1);
i f (maxpart >= MAX_PARTS) {
fprintf(stderr,"splitFile: file %s has too many sections",
filename);
exit(10);
}
i f ((parts[++maxpart] = fopen(filename,_access))==NULL) {
fprintf(stderr,"splitFile: error creating next section of file: \
%s", filename);
exit(10);
}
curpart=maxpart;
partsize[curpart] = 0;
}
b o o l splitFile::OpenNextPart()
{
char filename[1024];
sprintf(filename,"%s.%02d",_filename,maxpart+1);
u n s i g n e d l o n g thispartsize;
FILE *dummy;
i f ((dummy=fopen(filename,"r"))==NULL) r e t u r n f a l s e ;
fseek(dummy,0,SEEK_END);
thispartsize = ftell(dummy);
fclose(dummy);
i f (maxpart >= MAX_PARTS) {
fprintf(stderr,"splitFile: file %s has too many sections",
filename);
exit(10);
}
i f ((parts[++maxpart] = fopen(filename,_access))==NULL) {
fprintf(stderr,"splitFile: error opening next section of file: \
%s",filename);
exit(10);
}
curpart=maxpart;
partsize[curpart] = thispartsize;
return true;
}
b o o l splitFile::GotoNextPart( b o o l write_mode)
{
i f (parts[curpart+1] == NULL) {
i f (write_mode) CreateNextPart();
e l s e return f a l s e ;
} else {
curpart+=1;
fseek(parts[curpart],0,SEEK_SET);
}
return true;
}
u n s i g n e d l o n g splitFile::curfilepos()
{
148 Chapter B. FHP-Murϕ: the Verifier

r e t u r n ftell(parts[curpart]);
}
u n s i g n e d l o n g splitFile::getpartsize( i n t npart)
{
r e t u r n partsize[npart];
}
b o o l splitFile::open(char *filename, char* access)
{
strcpy(_access,access);
strcpy(_filename,filename);
i f (strchr(access,’r’)) w h i l e (OpenNextPart());
i f (maxpart<0) CreateNextPart();
curpart=0;
//if the file was opened with read as primary mode (r+)
//then ignore the user specified permanent flag and do not delete
//the opened file!
i f (strchr(access,’r’)) setPermanent( t r u e );
return true;
}
v o i d splitFile::close()
{
//all the file parts are opened and registered in the parts array
char filename[1024];
f o r ( i n t i=0; i<=maxpart; i++) {
i f (parts[i]!=NULL) {
fclose(parts[i]);
i f (!_permanent) {
sprintf(filename,"%s.%02d",_filename,i);
remove(filename);
}
}
}
memset(parts,0, s i z e o f (FILE*)*MAX_PARTS);
maxpart = -1;
curpart = -1;
_eof = t r u e ;
}
/*
* splitFile read and write routines are identical to the
* corresponding FILE* versions.
*
*/
u n s i g n e d l o n g splitFile::writebytes( v o i d * buffer, u n s i g n e d l o n g size)
{
u n s i g n e d l o n g inthispart, towrite,totalwritten=0,reminder=0;
u n s i g n e d l o n g pp=0;
w h i l e (size>0) {
inthispart= (_splitsize-curfilepos());
towrite = min(size,inthispart);
fwrite(((char*)buffer)+pp,1,towrite,parts[curpart]);
size-=towrite;
totalwritten+=towrite;
pp+=towrite;
i f (ftell(parts[curpart])>partsize[curpart])
partsize[curpart] = ftell(parts[curpart]);
i f (size>0) GotoNextPart( t r u e );
}
r e t u r n totalwritten;
}
B.1 C++ Code 149

u n s i g n e d l o n g splitFile::write( v o i d * buffer, u n s i g n e d l o n g size,


u n s i g n e d l o n g count)
{
u n s i g n e d l o n g totalwritten=0;
u n s i g n e d l o n g pp=0;
_eof= f a l s e ;
w h i l e (count>totalwritten) {
writebytes(((char*)buffer)+pp,size);
pp+=size;
totalwritten++;
}
r e t u r n totalwritten;
}
u n s i g n e d l o n g splitFile::readbytes( v o i d * buffer, u n s i g n e d l o n g size)
{
u n s i g n e d l o n g inthispart, toread, read, totalread=0, reminder=0;
u n s i g n e d l o n g pp=0;
w h i l e (size>0) {
inthispart = (partsize[curpart]-curfilepos());
toread = min(size,inthispart);
read = fread(((char*)buffer)+pp,1,toread,parts[curpart]);
size-=read;
totalread+=read;
pp+=read;
i f (size>0) {
i f (read==toread) {
i f (!GotoNextPart( f a l s e )) {
_eof= t r u e ;
break;
}
} else {
_eof= t r u e ;
break;
}
}
}
r e t u r n totalread;
}
u n s i g n e d l o n g splitFile::read( v o i d * buffer, u n s i g n e d l o n g size,
u n s i g n e d l o n g count)
{
u n s i g n e d l o n g read, totalread=0;
u n s i g n e d l o n g pp=0;
w h i l e (count>totalread) {
read = readbytes(((char*)buffer)+pp,size);
pp+=read;
i f (read!=size) break; //eof
totalread++;
}
r e t u r n totalread;
}
/*
* splitFile seek takes two unsigned parameters to specify
* the position - like read and write.
* The two-argument version assumes that the size is 1 byte
* (so actually it works like the FILE* seek function)
* To specify the seek direction, you can use the optional fourth
* parameter and set it to FORWARD or BACKWARD (default is DEFAULDIR,
150 Chapter B. FHP-Murϕ: the Verifier

see below).
* WARNING: by default seeking from SEEK_END assumes backward direction
* (i.e. you should not write seek(-5,SEEK_END) to move backward, but
* seek(5,SEEK_END)), whileas SEEK_SET and SEEK_CUR assume FORWARD
direction.
*
*/
v o i d splitFile::seek( u n s i g n e d l o n g count, i n t from)
{
seek(1,count,from);
}
b o o l splitFile::seekbytes( u n s i g n e d l o n g size, i n t direction, i n t & part
, u n s i g n e d l o n g & skip)
{
i n t i;
w h i l e (size>0) {
i f (getpartsize(part)-skip > size) {
skip += size;
return true;
} else {
size -= (getpartsize(part)-skip);
i f (direction == FORWARD) {
i f (part<maxpart) {
skip = 0;
part++;
} else {
skip = getpartsize(part);
return f a l s e ;
}
} e l s e i f (direction == BACKWARD) {
i f (part>0) {
skip = 0;
part--;
} else {
skip = 0;
return f a l s e ;
}
}
}
}
return true;
}
v o i d splitFile::seek( u n s i g n e d l o n g size, u n s i g n e d l o n g count, i n t from
, i n t direction)
{
i n t part, i;
u n s i g n e d l o n g skip=0;
b o o l result= t r u e ;
i f (from == SEEK_SET) {
part=0;
i f (direction == DEFAULTDIR) direction=FORWARD;
skip=0;
} e l s e i f (from == SEEK_END) {
part=maxpart;
i f (direction == DEFAULTDIR) direction=BACKWARD;
skip=0;
} e l s e { //SEEK_CUR
part=curpart;
i f (direction == DEFAULTDIR) direction=FORWARD;
i f (direction == FORWARD)
skip = curfilepos();
else
skip = getpartsize(part) - curfilepos();
B.1 C++ Code 151

}
f o r (i=0; (i<count) && result; i++)
result = seekbytes(size,direction,part,skip);
curpart=part;
i f (direction == FORWARD) {
fseek(parts[curpart],skip,SEEK_SET);
} e l s e i f (direction == BACKWARD) {
fseek(parts[curpart],-skip,SEEK_END);
}
_eof= f a l s e ;
}
/*
* splitFile tell returns the number of blocks of the sepcified size
* that precede the current file pointer. If size is not specified,
* the default is 1 byte, so tell acts like the standard ftell
* WARNING: if size is too small, the returned value may overflow
* WARNING: if the current file pointer is not a multiple of size,
* the returned value is rounded at the previous block.
*
*/
u n s i g n e d l o n g splitFile::tell(u n s i g n e d l o n g size)
{
u n s i g n e d l o n g total=0, skip=0;
f o r ( i n t i = 0; i<curpart; i++) {
total += (getpartsize(i)+skip) / size;
skip = (getpartsize(i)+skip) % size;
}
r e t u r n total+((curfilepos() + skip) / size);
}
b o o l splitFile::eof()
{
r e t u r n _eof;
}

B.1.15 A DDED FILE MU PROBSTACK . H

..
.
# i f n d e f PROBSTACK
# d e f i n e PROBSTACK
# i n c l u d e "splitFile.h"
s t r u c t state_prob_rule {
state _state;
double _prob_reach, _prob_f;
u n s i g n e d _rule;
state_prob_rule (state* s, double p_r, double p_f, u n s i g n e d r)
{_state = *s; _prob_reach=p_r; _prob_f=p_f; _rule=r;}
state_prob_rule () { };
};
/****************************************
The state stack.
****************************************/
c l a s s state_prob_rule_stack
{
152 Chapter B. FHP-Murϕ: the Verifier

# i f d e f HASHC
t y p e d e f u n s i g n e d l o n g Unsigned32;
#endif
protected:
state_prob_rule* stateArray; /* The actual array. */
/* max total entries (RAM + disk) of stack (equal to the verification
horizon) */
u n s i g n e d l o n g max_active_states;
/* index of the top of the stack */
u n s i g n e d l o n g front;
/* size of the stack in RAM */
u n s i g n e d l o n g size;
/* size for the stack cycling */
u n s i g n e d l o n g size_2;
u n s i g n e d l o n g num_elts;
u n s i g n e d disk2stack_cnt, stack2disk_cnt;
u n s i g n e d l o n g mem_used;
splitFile *paging_file;
v i r t u a l v o i d Stack2Disk();
v i r t u a l v o i d Disk2Stack();
i n l i n e b o o l Stack2Disk_needed() { r e t u r n front >= size;};
i n l i n e b o o l Disk2Stack_needed()
{ r e t u r n stack2disk_cnt != disk2stack_cnt && front == 0; };
public:
// initializers
state_prob_rule_stack( u n s i g n e d l o n g memory, u n s i g n e d l o n g bound);
// destructor
v i r t u a l ˜state_prob_rule_stack();
i n l i n e b o o l isempty( v o i d )
{ r e t u r n stack2disk_cnt != disk2stack_cnt && front == 0; };
i n l i n e u n s i g n e d l o n g NumElts() { r e t u r n num_elts;}
i n l i n e s t a t i c i n t BytesForOneState( v o i d );
// storing and removing elements
v i r t u a l v o i d push(state* e, double p_r, double p_f);
v i r t u a l state *pop( double &p_r, double &p_f);
v i r t u a l state *reset( l o n g level);
v i r t u a l state *top();
v i r t u a l double update_prob(double prob);
// hyp: this is called after the last pop
i n l i n e double final_prob() { r e t u r n stateArray[front]._prob_f;};
i n l i n e double get_prob() { r e t u r n stateArray[front - 1]._prob_f;};
i n l i n e u n s i g n e d l o n g memory_used() { r e t u r n mem_used;};
v i r t u a l u n s i g n e d NextRuleToTry();
v i r t u a l v o i d NextRuleToTry( u n s i g n e d r);
// printing routine
v o i d Print( v o i d );
v i r t u a l v o i d print_capacity( v o i d ) {};
};
#endif
# i f n d e f _FILTERQUEUE_
# d e f i n e _FILTERQUEUE_
s t r u c t state_and_probability_pair {
state _state;
double _probability;
B.1 C++ Code 153

state_and_probability_pair (state* s, double p)


{_state = *s; _probability=p;}
state_and_probability_pair () { };
};
/****************************************
The state queue.
****************************************/
c l a s s state_probability_queue
{
# i f d e f HASHC
t y p e d e f u n s i g n e d l o n g Unsigned32;
#endif
protected:
state_and_probability_pair* stateArray; /* The actual array. */
state_and_probability_pair *table;
c o n s t u n s i g n e d l o n g max_active_states; /* max size of queue */
u n s i g n e d l o n g front; /* index of first active state. */
u n s i g n e d l o n g rear; /* index of next free slot. */
unsigned long global_rear, global_front;
unsigned long num_elts_head, num_elts_tail;
unsigned long head_begin, tail_begin;
unsigned long head_size, tail_size;
splitFile *paging_file_top, *paging_file_bottom;
b o o l hashtable_lookup(state_and_probability_pair* &in);
v o i d enqueue_commit( state_and_probability_pair* e );
dynBitVec *Full;
u n s i g n e d l o n g hashtable_size;
u n s i g n e d l o n g hashtable_num_elts;
randomGen random;
public:
// initializers
state_probability_queue( u n s i g n e d l o n g mas );
// destructor
v i r t u a l ˜state_probability_queue();
// information interface
i n l i n e u n s i g n e d l o n g MaxElts( v o i d ) { r e t u r n max_active_states; }
u n s i g n e d l o n g NumElts( v o i d )
{ r e t u r n num_elts_head + num_elts_tail + global_rear +
global_front + hashtable_num_elts; }
i n l i n e b o o l isempty( v o i d );
i n l i n e s t a t i c i n t BytesForOneState( v o i d );
i n l i n e s t a t i c i n t BytesForOneState_hashtable( v o i d );
// storing and removing elements
v i r t u a l b o o l enqueue( state* &e, double p );
v i r t u a l state* dequeue( double &prob );
v i r t u a l state* top( double &prob );
v i r t u a l v o i d ReclaimFreeSpace();
v i r t u a l v o i d QueueEmptyFault();
i n l i n e v o i d CheckTable();
v i r t u a l u n s i g n e d NextRuleToTry()
// Uli: unsigned short -> unsigned
{
Error.Notrace("Internal: Getting next rule to try from a state q\
154 Chapter B. FHP-Murϕ: the Verifier

ueue instead of a state stack.");


r e t u r n 0;
}
v i r t u a l v o i d NextRuleToTry( u n s i g n e d r)
{
Error.Notrace("Internal: Setting next rule to try from a state q\
ueue instead of a state stack.");
}
// printing routine
v o i d Print( v o i d );
v i r t u a l v o i d print_capacity( v o i d )
{
cout << "\t* Capacity of queue for breadth-first search:\n"
<< "\t * " << max_active_states << " states in the queue.\n"
<< "\t * " << hashtable_size << " states in the cache.\n"
<< "\t * Change the constant gPercentActiveStates in mu_prolo\
g.inc\n"
<< "\t to increase this, if necessary.\n";
}
};
#endif

B.1.16 A DDED FILE MU PROBSTACK .C

..
.
# i n c l u d e "mu_probstack.h"
# i n c l u d e <math.h>
# d e f i n e min(a,b) (((a)<(b))?(a):(b))
# d e f i n e max(a,b) (((a)>(b))?(a):(b))
# d e f i n e DBG 0
# d e f i n e SPLITFILE_LEN (1024 * 0x100000L) /* 100MB */
# d e f i n e SFS_PAGING_FILE "SFS_PAGING_FILE"
char *make_unique_filename(char *basepathname)
{
s t a t i c char filenamebuffer[500];
//Maybe I can add a random seed...
sprintf(filenamebuffer,"%s_%s.swp", basepathname, PROTOCOL_NAME);
r e t u r n filenamebuffer;
}
state_prob_rule_stack::state_prob_rule_stack( u n s i g n e d l o n g memory,
u n s i g n e d l o n g bound )
: max_active_states(bound), front(0), disk2stack_cnt(0),
stack2disk_cnt(0), num_elts(0), mem_used(0)
{
i f (memory >= max_active_states* s i z e o f (state_prob_rule))
{
size = size_2 = max_active_states;
paging_file = NULL;
}
else
{
size = ( u n s i g n e d )(memory/ s i z e o f (state_prob_rule));
i f (size%2 == 1) size--;
size_2 = size / 2;
paging_file = new splitFile(SPLITFILE_LEN, f a l s e );
B.1 C++ Code 155

mem_used += s i z e o f (splitFile);
i f ( !(paging_file->open(make_unique_filename(SFS_PAGING_FILE),
"w+b")))
Error.Notrace("Internal: Error creating paging file for s_f_s.");
}
stateArray = new state_prob_rule[size];
mem_used += size* s i z e o f (state_prob_rule);
}
state_prob_rule_stack::˜state_prob_rule_stack()
{
d e l e t e [] stateArray;
i f (paging_file != NULL)
{
paging_file->close();
d e l e t e paging_file;
}
}
int
state_prob_rule_stack::BytesForOneState( v o i d )
{
r e t u r n s i z e o f (state_prob_rule);
}
void
state_prob_rule_stack::Print( v o i d )
{
u n s i g n e d l o n g i;
f o r ( i = 0; i < front; i++ )
cout << "ProbReach at " << i << ": " << stateArray[i]._prob_reach
<< "\n";
}
v o i d state_prob_rule_stack::push(state* e, double p_r, double p_f)
{
i f (Stack2Disk_needed())
Stack2Disk();
stateArray[front]._state = *e;
stateArray[front]._prob_reach = p_r;
stateArray[front]._prob_f = p_f;
stateArray[front]._rule = 0;
front++;
num_elts++;
}
state *state_prob_rule_stack::pop(double &p_r, double &p_f)
{
i f (Disk2Stack_needed())
Disk2Stack();
front--;
p_r = stateArray[front]._prob_reach;
p_f = stateArray[front]._prob_f;
num_elts--;
r e t u r n &(stateArray[front]._state);
}
state* state_prob_rule_stack::top()
{
i f (Disk2Stack_needed())
Disk2Stack();
r e t u r n &(stateArray[front - 1]._state);
}
156 Chapter B. FHP-Murϕ: the Verifier

v o i d state_prob_rule_stack::Stack2Disk()
{
stack2disk_cnt++;
paging_file->write(&stateArray[0], s i z e o f (state_prob_rule),
size_2);
memmove(stateArray, &stateArray[size_2],
size_2* s i z e o f (state_prob_rule));
front = size_2;
}
v o i d state_prob_rule_stack::Disk2Stack()
{
disk2stack_cnt++;
memmove(&stateArray[size_2], stateArray,
size_2* s i z e o f (state_prob_rule));
paging_file->seek((stack2disk_cnt - disk2stack_cnt)*
s i z e o f (state_prob_rule)*size_2, SEEK_SET);
paging_file->read(stateArray, s i z e o f (state_prob_rule), size_2);
paging_file->seek((stack2disk_cnt - disk2stack_cnt)*
s i z e o f (state_prob_rule)*size_2, SEEK_SET);
front = size_2;
}
u n s i g n e d state_prob_rule_stack::NextRuleToTry()
{
i f (Disk2Stack_needed())
Disk2Stack();
r e t u r n stateArray[front - 1]._rule;
}
v o i d state_prob_rule_stack::NextRuleToTry( u n s i g n e d r)
{
i f (Disk2Stack_needed())
Disk2Stack();
stateArray[front - 1]._rule = r;
}
/* Assumption: the slot to update corresponds to front - 1 */
double state_prob_rule_stack::update_prob(double p)
{
i f (Disk2Stack_needed())
Disk2Stack();
stateArray[front - 1]._prob_f += p;
r e t u r n stateArray[front - 1]._prob_f;
}
state *state_prob_rule_stack::reset( l o n g level)
{
i f (stack2disk_cnt != disk2stack_cnt)
{
f o r ( l o n g i = level - 1; i >= -1; i--)
{
i f (Disk2Stack_needed())
Disk2Stack();
front--;
num_elts--;
}
}
else
B.1 C++ Code 157

{
front -= level + 1;
num_elts -= level + 1;
}
r e t u r n &(stateArray[front]._state);
}
# d e f i n e min(a,b) (((a)<(b))?(a):(b))
# d e f i n e max(a,b) (((a)>(b))?(a):(b))
# d e f i n e DBG 0
# d e f i n e SPLITFILE_LEN (1024 * 0x100000L) /* 100MB */
# d e f i n e SFS_PAGING_FILE "SFS_PAGING_FILE"
# i f d e f MAX_HT_CHAIN_LENGTH
# undef MAX_HT_CHAIN_LENGTH
#endif
# d e f i n e MAX_HT_CHAIN_LENGTH 20
# define SFQ_PAGING_FILE_TOP "SFQ_PAGING_FILE_1"
# define SFQ_PAGING_FILE_BOTTOM "SFQ_PAGING_FILE_2"
# define SSQ_PAGING_FILE_TOP "SSQ_PAGING_FILE_1"
# define SSQ_PAGING_FILE_BOTTOM "SSQ_PAGING_FILE_2"
# define SFQ_HISTORY_FILE "SFQ_HISTORY_FILE"
state_probability_queue::state_probability_queue( u n s i g n e d l o n g mas)
: max_active_states((u n s i g n e d l o n g )(mas * gPercentActiveStates))
{
stateArray = new state_and_probability_pair[ max_active_states];
hashtable_size = NextPrime(( u n s i g n e d l o n g )mas);
table = new state_and_probability_pair[hashtable_size];
Full = new dynBitVec(hashtable_size);
paging_file_top = new splitFile(SPLITFILE_LEN, f a l s e );
paging_file_bottom = new splitFile(SPLITFILE_LEN, f a l s e );
i f (!(paging_file_top->open(make_unique_filename(
SFQ_PAGING_FILE_TOP), "w+b"))) {
Error.Notrace( "Internal: Error creating top paging file for s_f\
_q.");
}
i f (!(paging_file_bottom->open(make_unique_filename(
SFQ_PAGING_FILE_BOTTOM),"w+b"))) {
Error.Notrace( "Internal: Error creating bottom paging file.");
}
num_elts_head = num_elts_tail = 0;
head_begin = 0;
tail_begin = max_active_states/2;
head_size = max_active_states/2;
tail_size = max_active_states - head_size;
global_front=global_rear=front=rear=0;
}
state_probability_queue::˜state_probability_queue()
{
d e l e t e [] stateArray;
d e l e t e [] table;
d e l e t e Full;
paging_file_top->close(); //rmtmp();
paging_file_bottom->close(); //rmtmp();
158 Chapter B. FHP-Murϕ: the Verifier

d e l e t e paging_file_top;
d e l e t e paging_file_bottom;
}
b o o l state_probability_queue::isempty( v o i d )
{
r e t u r n (num_elts_head + num_elts_tail + global_rear +
global_front + hashtable_num_elts) == 0;
}
int
state_probability_queue::BytesForOneState( v o i d ) {
r e t u r n s i z e o f (state_and_probability_pair);
}
int
state_probability_queue::BytesForOneState_hashtable( v o i d ) {
r e t u r n s i z e o f (state_and_probability_pair);
}
void
state_probability_queue::Print( v o i d )
{
u n s i g n e d l o n g i;
f o r ( i = 0; i < num_elts_head; i++ )
{
// convert to print in unsigned long format?
cout << "State " << i << " [" << head_begin+i << "]:\n";
stateArray[ head_begin+i ]._state.print();
}
f o r ( i = 0; i < num_elts_tail; i++ )
{
// convert to print in unsigned long format?
cout << "State " << i << " [" << tail_begin+i << "]:\n";
stateArray[ tail_begin+i ]._state.print();
}
}
bool
state_probability_queue::enqueue( state*& e, double p )
{
i f (hashtable_num_elts >= hashtable_size) CheckTable();
state_and_probability_pair pair(e,p);
state_and_probability_pair *ppair = &pair;
i f (!hashtable_lookup(ppair)) {
ppair->_probability = p;
e = &(ppair->_state);
return true;
} else {
ppair->_probability+=p;
e = &(ppair->_state);
return f a l s e ;
}
}
void
state_probability_queue::enqueue_commit(state_and_probability_pair
*e)
{
i f ( num_elts_tail >= tail_size )
{
//memory full: reclaim more space by swapping out the current
//queue
ReclaimFreeSpace();
}
B.1 C++ Code 159

/*
at this point, ReclaimFreeSpace has obtained new space in the
queue and set the offsets (front, rear, ...) accordingly; so we
proceed with the insertion without checking...
*/
stateArray[ tail_begin + rear ] = *e;
rear++;
num_elts_tail++;
}
state*
state_probability_queue::dequeue( double &prob )
{
state* retval;
i f ( num_elts_head <= 0 )
{
QueueEmptyFault();
}
retval = &(stateArray[head_begin + front]._state);
prob = stateArray[head_begin + front]._probability;
front++;
num_elts_head--;
r e t u r n retval;
}
state*
state_probability_queue::top( double &prob )
{
i f ( num_elts_head <= 0 )
{
QueueEmptyFault();
}
prob = stateArray[head_begin + front]._probability;
r e t u r n &(stateArray[head_begin + front]._state);
}
v o i d state_probability_queue::ReclaimFreeSpace()
{
global_rear += paging_file_bottom->write(&stateArray[tail_begin],
s i z e o f (state_and_probability_pair),
tail_size);
num_elts_tail = 0;
rear = 0;
}
v o i d state_probability_queue::QueueEmptyFault()
{
//make sure we do not read garbage after the states
size_t read = paging_file_top->read(&stateArray[head_begin],
s i z e o f (state_and_probability_pair),min(global_front,head_size));
i f (read>0) { //ok, some states are swapped in
num_elts_head = read;
global_front -= read;
} e l s e i f (global_rear>0) {
//paging_file_top is empty, but paging_file_bottom is not
splitFile *fswap;
//swap files
fswap= paging_file_top;
paging_file_top = paging_file_bottom;
paging_file_bottom = fswap;
160 Chapter B. FHP-Murϕ: the Verifier

//move to the beginning of the queue


paging_file_top->seek(0,SEEK_SET);
paging_file_bottom->seek(0,SEEK_SET); //reset bottom queue
global_front = global_rear;
global_rear = 0; //bottom file is empty
//now bottom entries are top entries and bottom file is empty.
//Reload a block!
size_t read = paging_file_top->read(&stateArray[head_begin],
s i z e o f (state_and_probability_pair),
min(global_front,head_size));
num_elts_head = read;
global_front -= read;
} e l s e i f (num_elts_tail >0){
//paging_file_top AND paging_file_bottom are empty
/* the disk queue is ended. this means that the only states
we have to explore are the ones in the current tail window */
u n s i g n e d l o n g swap = tail_begin;
tail_begin = head_begin;
head_begin = swap;
swap=tail_size;
tail_size = head_size;
head_size = swap;
num_elts_head = num_elts_tail;
num_elts_tail = 0;
rear =0;
} else {
//no more states in both swap files, and the memory is empty:
//why do we call again?
Error.Notrace( "Internal: Attempt to read an empty state queue.");
}
front = 0;
}
bool
state_probability_queue::hashtable_lookup(state_and_probability_pair
*&in)
{
u n s i g n e d l o n g key = in->_state.hashkey();
u n s i g n e d l o n g h1 = key % hashtable_size;
u n s i g n e d l o n g h2 = 1 + key % ( hashtable_size - 1 );
u n s i g n e d l o n g h = h1;
u n s i g n e d l o n g probe = 0;
// no hash compaction, uses double hashing
b o o l empty, equal= FALSE;
w h i l e ( !(empty = (Full->get(h) == 0)) &&
!(equal = ( in->_state == table[h]._state )) &&
( probe < MAX_HT_CHAIN_LENGTH ) )
{
h = (h1 + probe * h2) % hashtable_size; // double hashing
probe++;
}
i f (empty) /* Go ahead and insert the element. */
{
table[h] = *in;
in = &table[h];
Full->set(h);
hashtable_num_elts++;
B.1 C++ Code 161

r e t u r n FALSE;
}
e l s e i f (equal)
{
in = &table[h];
r e t u r n TRUE;
}
else
{
//table is full,clear it!
CheckTable();
//the table now is empty: use the first slot available for
//this state
h = h1 % hashtable_size;
table[h] = *in;
in = &table[h];
Full->set(h);
hashtable_num_elts++;
r e t u r n FALSE;
}
r e t u r n FALSE;
}
v o i d state_probability_queue::CheckTable()
{
//1. move all the table states in the queue
u n s i g n e d l o n g moved=0;
u n s i g n e d l o n g i;
f o r (i=0 ; i< hashtable_size; i++) {
i f (Full->get(i)==1) {
enqueue_commit(&table[i]);
moved++; //for dbg only
}
}
//2. clear the table
Full->clearall();
hashtable_num_elts=0;
}

B.1.17 M ODIFICATIONS TO MU SYSTEM . H

..
.
c l a s s StateManager
{
state_set *the_states; // the set of states found.
state_probability_queue *queue; // the queue for active states.
state_prob_rule_stack *stack; // the stack for active states.
u n s i g n e d l o n g NumStates;
double harmonic(double n); // return harmonic number H_n
// Uli: for omission probability calculation
l o n g statesCurrentLevel; // number of states in the level that
// is currently expanded
l o n g statesNextLevel; // number of states in the next level
l o n g currentLevel; // level that is currently expanded
// (startstates: level 0)
162 Chapter B. FHP-Murϕ: the Verifier

double pno; // Pr(particular state not omitted)


u n s i g n e d l o n g num_elts; // number of states enququed so far
// (substitutes the_states->NumElts())
public:
StateManager(double , u n s i g n e d long , u n s i g n e d );
˜StateManager();
// the cache for already computed subformulas probabilities
aux_cache *subf_cache;
u n s i g n e d Add(state * s, double p, b o o l valid, b o o l permanent);
b o o l QueueIsEmpty();
state * QueueTop(double &);
state *QueueDequeue(double &, double &);
state *QueueDequeue(double &);
v o i d QueuePush(state *, double , double );
state * ResetQueue( l o n g );
double QueueUpdateProb(double );
double QueueFinalProb();
double QueueGetProb();
u n s i g n e d NextRuleToTry(); // Uli: unsigned short -> unsigned
v o i d NextRuleToTry( u n s i g n e d r);
// Uli: routines for omission probability calculation & printing
i n t CheckLevel();
v o i d PrintProb();
v o i d print_capacity();
v o i d print_all_states();
v o i d print_trace(StatePtr p); // changes by Uli
v o i d print_trace_aux(StatePtr p);
u n s i g n e d l o n g NumElts();
v o i d IncrNumElts();
u n s i g n e d l o n g NumEltsReduced(); // Uli
u n s i g n e d l o n g QueueNumElts() { r e t u r n queue->NumElts();}
l o n g GetCurrentLevel() { r e t u r n currentLevel;}
};
// extern class StartStateGenerator;
c l a s s StartStateGenerator;
/************************************************************/
c l a s s StartStateManager
{
s t a t i c u n s i g n e d s h o r t numstartstates;
u n s i g n e d s h o r t what_startstate; // for info at Error
StartStateGenerator * generator;
randomGen random; // Uli: random number generator
public:
StartStateManager();
state * RandomStartState();
u n s i g n e d AllStartStates();
state * NextStartState();
state * StartState();
char * LastStateName();
// probabilistic counterpart of LastStateName()
double LastStateProb();
char * StateName(StatePtr p); // changes by Uli
};
// extern class NextStateGenerator;
c l a s s NextStateGenerator;
/************************************************************/
c l a s s RuleManager
{
double what_prob; // probabilistic counterpart of what_rule
u n s i g n e d l o n g rules_fired;
B.1 C++ Code 163

u n s i g n e d l o n g * NumTimesFired; /* array for storing the number


of times fired for each rule */
u n s i g n e d AllNextStates(setofrules * fire);
randomGen random; // Uli: random number generator
u n s i g n e d what_rule; // for execution and info at Error
NextStateGenerator * generator;
public:
RuleManager();
˜RuleManager();
state * RandomNextState();
/* moved to the public part */
setofrules * EnabledTransition();
state * NextState();
/* moved to the public part */
state * SeqNextState(double &);
u n s i g n e d AllNextStates();
v o i d ResetRuleNum();
v o i d SetRuleNum( u n s i g n e d r);
v o i d IncRuleNum();
u n s i g n e d GetRuleNum();
char * LastRuleName();
// probabilistic counterpart of LastRuleName()
double LastRuleProb();
u n s i g n e d l o n g NumRulesFired();
v o i d print_rules_information();
v o i d print_world_to_state(StatePtr p, b o o l fullstate);
// changes by Uli
};
/************************************************************/
c l a s s PropertyManager
{
u n s i g n e d s h o r t what_invariant; // for info at Error
public:
PropertyManager();
b o o l CheckInvariants();
char * LastInvariantName();
};
/************************************************************/
c l a s s SymmetryManager
{
state_set *debug_sym_the_states; // the set of states found without
sym.
public:
SymmetryManager();
};
/************************************************************/
c l a s s POManager // Partial Order
{
rule_matrix *conflict_matrix;
public:
POManager();
};
/************************************************************/
c l a s s AlgorithmManager
{
private:
b o o l verify_bpmc_rec(state *, c o n s t pctlformrec *);
b o o l evalX(state *, c o n s t pctlformrec *);
b o o l evalU(state *, c o n s t pctlformrec *);
i n t NumberUntil( c o n s t pctlformrec *, i n t );
u n s i g n e d stack_size( c o n s t pctlformrec *);
double update_prob_loc(bool , double , double );
164 Chapter B. FHP-Murϕ: the Verifier

b o o l may_exit( c o n s t pctlformrec *, double , l o n g );


double fin_prob;
public:
AlgorithmManager();
˜AlgorithmManager();// Added AlgorithmManager destructor
v o i d verify_bfs();
v o i d verify_dfs();
v o i d simulate();
v o i d verify_bpmc();
b o o l IsSafety();
};
/************************************************************/
// manager for all startstate related operation
StartStateManager *StartState;
// manager for all rule related operation
RuleManager *Rules;
// manager for all property related operation
PropertyManager *Properties;
// manager for all state related information
StateManager *StateSet;
// manager for all symmetry information
SymmetryManager *Symmetry;
// manager for all symmetry information
POManager *PO;
// manager for all diagnostic messages
ReportManager *Reporter;
// manager for all algorithm related issue
AlgorithmManager *Algorithm;
// general error handler.
Error_handler Error;
// the record of the arguments.
argclass *args;
// current state at the beginning of the rule-firing
state *curstate;
// probability of curstate at the beginning of the rule-firing
double curstate_p;
// probability for error state
double E_p = 0;
// Uli: buffer for doing all state manipulation
state * c o n s t workingstate = new state;
// the set of global variables.
world_class theworld;
// working on startstate, rule or invariant
i n t category;
# i f d e f HASHC
// Uli: manager for trace info file
TraceFileManager* TraceFile;
#endif
// Uli: number of the current state for trace info file
u n s i g n e d l o n g NumCurState;

B.1.18 M ODIFICATIONS TO MU SYSTEM .C

..
.
/************************************************************/
/* StateManager */
/************************************************************/
StateManager::StateManager(double fraction, u n s i g n e d l o n g memory,
B.1 C++ Code 165

u n s i g n e d horizon)
: NumStates(0), stack(NULL)
{
i f (args->safety.value) {
NumStates = memory;
queue = new state_probability_queue(NumStates);
stack = NULL;
} else {
/* horizon is the number of transitions, here we need the number
of states */
stack = new state_prob_rule_stack((u n s i g n e d l o n g )
(fraction*memory), horizon+1);
subf_cache = new aux_cache(memory - stack->memory_used(),
4*horizon);
queue = NULL;
}
}
StateManager::˜StateManager()
{
i f (stack != NULL)
{
d e l e t e stack;
d e l e t e subf_cache;
}
i f (queue != NULL) d e l e t e queue;
}
u n s i g n e d StateManager::Add(state * s, double p, b o o l valid,
b o o l permanent)
{
i f (Properties->CheckInvariants()) {
E_p += p;
/* even if the max depth has not been reached, there cases in
which we can terminate */
i f ( (mu_pctl_formula.ord == PCTL_G &&
E_p > mu_pctl_formula.prob_bound)
|| (mu_pctl_formula.ord == PCTL_GEQ &&
E_p >= mu_pctl_formula.prob_bound))
r e t u r n 5; /* formula is valid, level not completed */
else
i f ( (mu_pctl_formula.ord == PCTL_L &&
E_p >= mu_pctl_formula.prob_bound)
|| (mu_pctl_formula.ord == PCTL_LEQ &&
E_p > mu_pctl_formula.prob_bound))
r e t u r n 6; /* formula is not valid, level not completed */
r e t u r n 0; //state not added
}
b o o l notpresent = queue->enqueue(s,p);
i f (notpresent) {
num_elts++;
i f ( args->trace_all.value ) Reporter->print_trace_all();
statesNextLevel++;
Reporter->print_progress(currentLevel);
r e t u r n 1;
} e l s e r e t u r n 0;
}
b o o l StateManager::QueueIsEmpty()
{
i f (stack)
r e t u r n stack->isempty();
else
r e t u r n queue->isempty();
}
166 Chapter B. FHP-Murϕ: the Verifier

state * StateManager::QueueTop( double &p)


{
i f (stack)
r e t u r n stack->top();
else
r e t u r n queue->top(p);
}
state *StateManager::QueueDequeue(double &p_r, double &p_f)
{
r e t u r n stack->pop(p_r, p_f);
}
state *StateManager::QueueDequeue(double &p)
{
r e t u r n queue->dequeue(p);
}
state *StateManager::ResetQueue( l o n g level)
{
stack->reset(level);
}
v o i d StateManager::QueuePush(state *s, double p_r, double p_f)
{
r e t u r n stack->push(s, p_r, p_f);
}
double StateManager::QueueFinalProb()
{
r e t u r n stack->final_prob();
}
double StateManager::QueueGetProb()
{
r e t u r n stack->get_prob();
}
// Uli: unsigned short -> unsigned
u n s i g n e d StateManager::NextRuleToTry()
{
r e t u r n stack->NextRuleToTry();
}
v o i d StateManager::NextRuleToTry( u n s i g n e d r)
{
stack->NextRuleToTry(r);
}
double StateManager::QueueUpdateProb(double p)
{
r e t u r n stack->update_prob(p);
}
# i n c l u d e <math.h>
double StateManager::harmonic( double n)
// return harmonic number H_n
{
r e t u r n (n<1) ? 0 :
log(n) + 0.577215665 + 1/(2*n) - 1/(12*n*n);
}
// check if we are done with the level currently expanded
i n t StateManager::CheckLevel()
{
i f (--statesCurrentLevel <= 0)
// all the states of the current level have been expanded
{
//filter the level states & load the queue
B.1 C++ Code 167

queue->CheckTable();
// proceed to next level
statesCurrentLevel = statesNextLevel;
statesNextLevel = 0;
// check if there are states in the following level
i f (statesCurrentLevel!=0)
{
currentLevel++;
i f (currentLevel > mu_pctl_formula.until_bound) {
// stop exploration
i f ( (mu_pctl_formula.ord == PCTL_G &&
E_p > mu_pctl_formula.prob_bound)
|| (mu_pctl_formula.ord == PCTL_GEQ &&
E_p >= mu_pctl_formula.prob_bound)
|| (mu_pctl_formula.ord == PCTL_L &&
E_p < mu_pctl_formula.prob_bound)
|| (mu_pctl_formula.ord == PCTL_LEQ &&
E_p <= mu_pctl_formula.prob_bound))
r e t u r n 3; /* formula is valid */
else
r e t u r n 4; /* formula is not valid */
}
}
Reporter->print_progress(currentLevel, args->fpel.value, t r u e );
}
r e t u r n 0;
} /* CheckLevel() */
# i f d e f HASHC
v o i d StateManager::PrintProb()
{
}
#endif
v o i d StateManager::print_capacity()
{
}
v o i d StateManager::print_all_states()
{
}
u n s i g n e d l o n g StateManager::NumElts()
{
i f (stack)
r e t u r n NumStates;
else
r e t u r n num_elts; //gdp
}
v o i d StateManager::IncrNumElts()
{
NumStates++;
}
u n s i g n e d l o n g StateManager::NumEltsReduced()
{
r e t u r n num_elts - (queue->NumElts() + statesNextLevel); //gdp
}
v o i d StateManager::print_trace_aux(StatePtr p) // changes by Uli
{
}
v o i d StateManager::print_trace(StatePtr p)
{
}
168 Chapter B. FHP-Murϕ: the Verifier

/************************************************************/
/* StartStateManager */
/************************************************************/
StartStateManager::StartStateManager()
{
generator = new StartStateGenerator;
}
state *
StartStateManager::RandomStartState()
{
what_startstate = ( u n s i g n e d s h o r t )(random. n e x t ()%numstartstates);
r e t u r n StartState();
}
unsigned
StartStateManager::AllStartStates()
{
state *nextstate = NULL;
double sop = ( double )0; // sum of probability of startstates
u n s i g n e d ret;
f o r (what_startstate=0; what_startstate < numstartstates;
what_startstate++)
{
sop += startstates[what_startstate].prob_bound;
nextstate = StartState();
// nextstate points to internal data at theworld.getstate()
i f ((ret = StateSet->Add(nextstate,
startstates[what_startstate].prob_bound,
FALSE, TRUE)) > 1)
r e t u r n ret;
}
i f (fabsl(sop - (double )1) >= 10*DBL_EPSILON)
Error.Error("Sum of probability of start states is equal to %le.",
sop);
r e t u r n 0;
}
state *
StartStateManager::NextStartState()
{
s t a t i c i n t next_startstate=0;
i f (next_startstate >= numstartstates) r e t u r n NULL;
what_startstate = next_startstate++;
r e t u r n StartState();
}
state *
StartStateManager::StartState()
{
state *next_state = NULL;
category = STARTSTATE;
// preparation
theworld.reset();
// fire state rule
generator->Code(what_startstate);
// print verbose message
i f (args->verbose.value > 4 || args->use_verbose_from_state.value)
Reporter->print_fire_startstate();
// Uli: invariant check moved
// Uli: mark as startstate
workingstate->previous.clear();
B.1 C++ Code 169

r e t u r n workingstate;
}
char *
StartStateManager::LastStateName()
{
r e t u r n generator->Name(what_startstate);
}
double
StartStateManager::LastStateProb()
{
r e t u r n startstates[what_startstate].prob_bound;
}
char *
StartStateManager::StateName(StatePtr p)
{
state nextstate;
i f (!p.isStart())
Error.Notrace("Internal: Cannot find startstate name for non sta\
rtstate");
f o r (what_startstate=0; what_startstate < numstartstates;
what_startstate++)
{
StartState();
StateCopy(&nextstate, workingstate);
i f (StateEquivalent(&nextstate, p))
r e t u r n LastStateName();
}
Error.Notrace("Internal: Cannot find startstate name for funny st\
artstate");
r e t u r n NULL;
}
/************************************************************/
/* RuleManager */
/************************************************************/
RuleManager::RuleManager() : rules_fired(0)
{
NumTimesFired = new u n s i g n e d l o n g [RULES_IN_WORLD];
generator = new NextStateGenerator;
// initialize check timesfired
f o r ( i n t i=0; i<RULES_IN_WORLD; i++)
NumTimesFired[i]=0;
};
RuleManager::˜RuleManager()
{
d e l e t e [ OLD_GPP(RULES_IN_WORLD) ] NumTimesFired;
}
void
RuleManager::ResetRuleNum()
{
what_rule = 0;
}
void
RuleManager::SetRuleNum( u n s i g n e d r)
{
what_rule = r;
}
unsigned
RuleManager::GetRuleNum()
{
r e t u r n what_rule;
170 Chapter B. FHP-Murϕ: the Verifier

}
void
RuleManager::IncRuleNum()
{
what_rule++;
}
state *
RuleManager::SeqNextState(double &nextprob)
{
state * ret;
i f (!args->state_compr.value)
{
//get the current rule number from the stack
what_rule = StateSet->NextRuleToTry();
//starting from what_rule, finds the next valid rule index (could
//be also the current one)
generator->SetNextEnabledRule(what_rule, nextprob);
i f ( what_rule<numrules )
{
ret = NextState();
//set the next rule number on the stack
StateSet->NextRuleToTry(what_rule+1);
r e t u r n ret;
}
else
r e t u r n NULL;
}
else
{
b o o l *what_rules = new b o o l [numrules];
u n s i g n e d num_found = 0;
u n s i g n e d to_be_found = StateSet->NextRuleToTry();
double nextprob_sum;
// buffer for workingstate
s t a t i c state *originalstate = new state;
// state to be compared
s t a t i c state *st_to_be_cmprd = new state;
f o r (u n s i g n e d i = 0; i < numrules; i++)
what_rules[i] = f a l s e ;
// make copy of workingstate
StateCopy(originalstate, workingstate);
f o r (what_rule = 0, generator->SetNextEnabledRule(what_rule,
nextprob);
what_rule < numrules && num_found != to_be_found + 1;
what_rule++,
generator->SetNextEnabledRule(what_rule, nextprob))
{
i f (!what_rules[what_rule])
{
u n s i g n e d what_rule_tmp = what_rule;
i f (num_found == to_be_found)
nextprob_sum = nextprob;
what_rules[what_rule] = t r u e ;
ret = NextState();
StateCopy(st_to_be_cmprd, ret);
// restore workingstate
StateCopy(workingstate, originalstate);
f o r (what_rule++, generator->SetNextEnabledRule(what_rule,
nextprob);
what_rule < numrules;
what_rule++,
generator->SetNextEnabledRule(what_rule, nextprob))
{
B.1 C++ Code 171

i f (!what_rules[what_rule])
{
ret = NextState();
i f (StateCmp(ret, st_to_be_cmprd) == 0)
{
what_rules[what_rule] = t r u e ;
i f (num_found == to_be_found)
nextprob_sum += nextprob;
}
// restore workingstate
StateCopy(workingstate, originalstate);
}
}
what_rule = what_rule_tmp;
num_found++;
}
}
i f (num_found != to_be_found + 1)
ret = NULL;
else
{
StateCopy(ret, st_to_be_cmprd);
nextprob = nextprob_sum;
}
StateSet->NextRuleToTry(to_be_found + 1);
d e l e t e [] what_rules;
r e t u r n ret;
}
}
state *
RuleManager::RandomNextState()
{
u n s i g n e d PickARule;
setofrules ret;
s t a t i c state *originalstate = new state;
i n t i;
double sop = 0.0;
// save workingstate
StateCopy(originalstate, workingstate);
category = CONDITION;
what_prob = 0.0;
ret.removeall();
// record what kind of analysis is currently carried out
category = CONDITION;
// get enabled
f o r ( what_rule=0, i=0; what_rule<numrules; what_rule++)
{
generator->SetNextEnabledRule(what_rule, what_prob);
i f ( what_rule<numrules ) {
cout << "Rule " << i++ << " with probability " << what_prob
<< endl;
ret.add(what_rule, what_prob);
sop += what_prob;
}
}
i f (fabsl(sop - (double )1) >= 10*DBL_EPSILON) {
cout << "Sum of probabilities is " << sop << endl;
exit(1);
}
cout << "Give a number between 0 and " << ret.size() - 1
<< ": ";
cout.flush();
172 Chapter B. FHP-Murϕ: the Verifier

scanf("%u", &PickARule);
what_rule = ret.getnthrule(PickARule);
what_prob = ret.prob(what_rule);
category = RULE;
generator->Code(what_rule);
curstate = workingstate;
rules_fired++;
// print verbose message
i f (!args->full_trace.value)
Reporter->print_fire_rule_diff(originalstate, what_prob);
e l s e Reporter->print_fire_rule(what_prob);
r e t u r n curstate;
}
unsigned
RuleManager::AllNextStates()
{
setofrules * fire;
// get set of rules to fire
fire = EnabledTransition();
// generate the set of next states
r e t u r n AllNextStates(fire);
}
/****************************************
checks probability sum to be 1, and returns
next states
****************************************/
setofrules *
RuleManager::EnabledTransition()
{
s t a t i c setofrules ret;
i n t p; // Priority of the current rule
# i f n d e f PROBEQUALK
double sop = ( double )0; // sum of probability of active transitions
#endif
ret.removeall();
// record what kind of analysis is currently carried out
category = CONDITION;
// get enabled
f o r ( what_rule=0; what_rule<numrules; what_rule++)
{
# i f n d e f PROBEQUALK
generator->SetNextEnabledRule(what_rule, what_prob);
#else
generator->SetNextEnabledRule(what_rule);
#endif
i f ( what_rule<numrules ) {
# i f n d e f PROBEQUALK
ret.add(what_rule, what_prob);
sop += what_prob;
#else
ret.add(what_rule);
#endif
}
}
# i f n d e f PROBEQUALK
i f (fabsl(sop - (double )1) >= 10*DBL_EPSILON)
{
printf("Starting from state\n");
theworld.print();
Error.Error("Sum of probability of active transitions is equal to\
B.1 C++ Code 173

%le.",sop);
}
#endif
r e t u r n &ret;
}
// Uli: corrected a memory-leak, improved performance
unsigned
RuleManager::AllNextStates(setofrules * fire)
{
// buffer for workingstate
s t a t i c state * originalstate = new state;
state * nextstate;
u n s i g n e d ret = 0;
// make copy of workingstate
StateCopy(originalstate, workingstate);
f o r ( what_rule=0; what_rule<numrules; what_rule++)
{
i f (fire->in(what_rule))
{
/* save rule probability BEFORE updating the current state in
NextState(). This is needed since the probability of a rule
can be a function of the verifier state!
*/
# i f n d e f PROBEQUALK
what_prob = fire->prob(what_rule);
#else
what_prob = ((double )1)/fire->size();
#endif
//get the state reached by the rule what_rule
nextstate = NextState();
i f ((ret = StateSet->Add(nextstate, curstate_p* what_prob, TRUE,
TRUE)) > 1)
r e t u r n ret;
// restore workingstate
StateCopy(workingstate, originalstate);
}
}
r e t u r n ret;
}
// the following global variables have been set:
// theworld, curstate and what_rule
state *
RuleManager::NextState()
{
category = RULE;
// fire rule
generator->Code(what_rule);
rules_fired++;
// update timesfired record
NumTimesFired[what_rule]++;
// print verbose message
i f (args->verbose.value > 4 || (args->use_verbose_from_state.value
&& StateSet->NumElts() >= args->verbose_from_state.value))
Reporter->print_fire_rule();
// get next state
# i f d e f HASHC
i f (args->trace_file.value)
workingstate->previous.set(NumCurState);
else
174 Chapter B. FHP-Murϕ: the Verifier

#endif
workingstate->previous.set(curstate);
r e t u r n workingstate;
}
void
RuleManager::print_world_to_state(StatePtr p, b o o l fullstate)
{
}
char *
RuleManager::LastRuleName()
{
r e t u r n generator->Name(what_rule);
}
double
RuleManager::LastRuleProb()
{
r e t u r n what_prob;
}
u n s i g n e d l o n g RuleManager::NumRulesFired()
{
r e t u r n rules_fired;
}
void
RuleManager::print_rules_information()
{
b o o l exist;
i f (args->print_rule.value)
{
cout << "Rules Information:\n\n";
f o r ( i n t i=0; i<RULES_IN_WORLD; i++)
cout << "\tFired " << NumTimesFired[i] << " times\t- Rule \""
<< generator->Name(i)
<< "\"\n";
}
else
{
f o r ( i n t i=0; i<RULES_IN_WORLD; i++)
i f (NumTimesFired[i]==0)
exist = TRUE;
i f (exist)
cout << "Analysis of State Space:\n\n"
<< "\tThere are rules that are never fired.\n";
}
}
/************************************************************/
/* PropertyManager */
/************************************************************/
PropertyManager::PropertyManager()
{
}
bool
PropertyManager::CheckInvariants()
{
r e t u r n mu_pctl_formula.subf2->condition();
}
char *
PropertyManager::LastInvariantName()
{
r e t u r n "";
}
B.1 C++ Code 175

/************************************************************/
/* SymmetryManager */
/************************************************************/
SymmetryManager::SymmetryManager()
{
}
/************************************************************/
/* POManager */
/************************************************************/
POManager::POManager()
{
}
/************************************************************/
/* AlgorithmManager */
/************************************************************/
AlgorithmManager::AlgorithmManager()
{
// create managers
StartState = new StartStateManager;
Rules = new RuleManager;
Reporter = new ReportManager;
# i f d e f HASHC
h3 = new hash_function(BLOCKS_IN_WORLD);
#endif
Reporter->CheckConsistentVersion();
i f (args->main_alg.mode!=argmain_alg::Nothing)
Reporter->print_header();
Reporter->print_algorithm();
i f (!args->simulate_prob.value && args->safety.value) {
StateSet = new StateManager(0.0,
NumStatesGivenBytes(args->mem.value),
0);
StateSet->print_capacity();
} else {
StateSet = NULL;
}
Reporter->print_warning();
signal(SIGFPE, &catch_div_by_zero);
fin_prob = -1;
};
// Added AlgorithmManager destructor
AlgorithmManager::˜AlgorithmManager()
{
d e l e t e StartState;
d e l e t e Rules;
d e l e t e Reporter;
# i f d e f HASHC
d e l e t e h3;
#endif
i f (StateSet != NULL) d e l e t e StateSet;
};
/****************************************
The BFS verification main routines:
void verify_bfs_standard()
-- future extension:
-- void verify_bfs_gode_dl()
-- void verify_bfs_sleepset_rr()
176 Chapter B. FHP-Murϕ: the Verifier

-- void verify_bfs_gode_sleepset_dl()
****************************************/
void
AlgorithmManager::verify_bfs()
{
// Use Global Variables: what_rule, curstate, theworld, queue,
// the_states
setofrules fire; // set of rule to be fired
u n s i g n e d ret;
i n t clres;
// print verbose message
i f (args->verbose.value || args->verbose_from_state.value)
Reporter->print_verbose_header();
cout.flush();
theworld.to_state(NULL); // trick : marks variables in world
// Generate all start state
i f (ret = StartState->AllStartStates())
clres = ret;
else {
// level monitoring
StateSet->CheckLevel();
// search state space
w h i l e (!StateSet->QueueIsEmpty())
{
// get and remove a state from the queue
// please make sure that global variable curstate does not change
// throughout the iteration
curstate = StateSet->QueueDequeue(curstate_p);
NumCurState++;
StateCopy(workingstate, curstate);
// print verbose message
i f (args->verbose.value || args->use_verbose_from_state.value)
Reporter->print_curstate();
// generate all next state
i f ((clres = Rules->AllNextStates()) > 1)
break;
// omission probability calculation and level monitoring
i f ( (clres = StateSet->CheckLevel()) != 0)
/* stop visit */
break;
} // while
} // else
printf("The probability of the formula is %.10lg\n", E_p);
Reporter->print_final_report(clres);
}
/****************************************
The DFS verification routine:
void verify_dfs()
-- not changed yet
****************************************/
void
AlgorithmManager::verify_dfs()
{
}
b o o l AlgorithmManager::IsSafety()
{
r e t u r n (mu_pctl_formula.subf2 &&
mu_pctl_formula.subf2->type == AP_PCTL &&
B.1 C++ Code 177

mu_pctl_formula.subf1 &&
mu_pctl_formula.subf1->type == AP_PCTL);
}
/****************************************
The BPMC main verification routine:
void verify_bpmc()
****************************************/
v o i d AlgorithmManager::verify_bpmc()
{
// print verbose message
i f (args->verbose.value > 4) Reporter->print_verbose_header();
theworld.to_state(NULL); // trick : marks variables in world
curstate = StartState->NextStartState(); //only one allowed
fin_prob = -1;
b o o l result = verify_bpmc_rec(curstate, &mu_pctl_formula);
i f (fin_prob != -1)
printf("The probability of the formula is %.10lg\n", fin_prob);
i f (result)
Reporter->print_final_report(3);
else
Reporter->print_final_report(4);
}
/****************************************
The BPMC recursive verification routine:
void verify_bpmc_rec()
****************************************/
b o o l AlgorithmManager::verify_bpmc_rec(state *s,
c o n s t pctlformrec *f)
{
s w i t c h (f->type)
{
c a s e AP_PCTL :
StateCopy(workingstate, s);
r e t u r n f->condition();
c a s e AND_PCTL :
r e t u r n (verify_bpmc_rec(s, f->subf1) &&
verify_bpmc_rec(s, f->subf2));
c a s e OR_PCTL :
r e t u r n (verify_bpmc_rec(s, f->subf1) ||
verify_bpmc_rec(s, f->subf2));
c a s e IMPL_PCTL :
r e t u r n (!verify_bpmc_rec(s, f->subf1) ||
verify_bpmc_rec(s, f->subf2));
c a s e NOT_PCTL :
r e t u r n (!verify_bpmc_rec(s, f->subf1));
c a s e NEXT_PCTL :
r e t u r n (evalX(s, f));
c a s e UNTIL_PCTL :
r e t u r n (evalU(s, f));
default :
Error.Notrace("Internal error: invalid value %d for f->type",
f->type);
r e t u r n FALSE;
}
}
b o o l AlgorithmManager::evalX(state *s, c o n s t pctlformrec *f)
{
setofrules * fire;
StateCopy(workingstate, s);
fire = Rules->EnabledTransition();
// buffer for workingstate
178 Chapter B. FHP-Murϕ: the Verifier

s t a t i c state * originalstate = new state;


state * nextstate;
// make copy of workingstate
StateCopy(originalstate, workingstate);
double sum = 0.0;
f o r (Rules->ResetRuleNum(); Rules->GetRuleNum() < numrules;
Rules->IncRuleNum())
{
i f (fire->in(Rules->GetRuleNum()))
{
nextstate = Rules->NextState();
i f (verify_bpmc_rec(nextstate, f->subf1))
sum += fire->prob(Rules->GetRuleNum());
StateCopy(workingstate, originalstate); // restore workingstate
}
}
s w i t c h (f->ord)
{
c a s e PCTL_L : r e t u r n sum < f->prob_bound;
c a s e PCTL_LEQ : r e t u r n sum <= f->prob_bound;
c a s e PCTL_G : r e t u r n sum > f->prob_bound;
c a s e PCTL_GEQ : r e t u r n sum >= f->prob_bound;
d e f a u l t : Error.Notrace("Internal error: invalid value %d for\
f->ord", f->ord); r e t u r n FALSE;
}
}
i n t AlgorithmManager::NumberUntil( c o n s t pctlformrec *f, i n t ret)
{
i f (f->type == UNTIL_PCTL)
ret++;
i f (f->subf1 != NULL)
ret = NumberUntil(f->subf1, ret);
i f (f->subf2 != NULL)
ret = NumberUntil(f->subf2, ret);
r e t u r n ret;
}
u n s i g n e d AlgorithmManager::stack_size( c o n s t pctlformrec *f)
{
i f (f->type != AP_PCTL)
{
u n s i g n e d k1 = stack_size(f->subf1);
u n s i g n e d k2 = f->subf2 != NULL? stack_size(f->subf2) : 0;
r e t u r n f->until_bound + (k1 > k2 ? k1 : k2);
}
else
r e t u r n 0;
}
double AlgorithmManager::update_prob_loc( b o o l tmp_psi,
double globreachprob,
double cache_probability)
{
double mult;
i f (cache_probability != -1.0)
mult = cache_probability;
e l s e i f (!tmp_psi)
mult = 0.0;
else
mult = 1.0;
r e t u r n globreachprob*mult;
}
B.1 C++ Code 179

b o o l AlgorithmManager::may_exit( c o n s t pctlformrec *f,


double fin_prob_loc, l o n g level)
{
r e t u r n (!args->disable_exit.value &&
(
( (f->ord == PCTL_L && fin_prob_loc >= f->prob_bound)
|| (f->ord == PCTL_LEQ && fin_prob_loc > f->prob_bound)
|| (f->ord == PCTL_G && fin_prob_loc > f->prob_bound)
|| (f->ord == PCTL_GEQ && fin_prob_loc >= f->prob_bound)
) || (level == 0 &&
( (f->ord == PCTL_L && fin_prob >= f->prob_bound)
|| (f->ord == PCTL_LEQ && fin_prob > f->prob_bound)
|| (f->ord == PCTL_G && fin_prob > f->prob_bound)
|| (f->ord == PCTL_GEQ && fin_prob >= f->prob_bound)))
)
);
}
/* It works only if there is only one initial state; the final
probability of the formula will be put in fin_prob */
b o o l AlgorithmManager::evalU(state *s, c o n s t pctlformrec *f)
{
i f (StateSet == NULL)
{
//int number_until = NumberUntil(f, 0);
/* fraction of memory to be used for the stack */
double fraction = 1.0 - NumberUntil(f, 0)/4.0;
//double fraction = 0.7;
StateSet = new StateManager((fraction >= 0.5? fraction : 0.5),
args->mem.value, stack_size(f));
}
state_prob_hor_form s_p_h_f(s, 0.0, f->until_bound, f);
//unsigned ht_chain = 5*f->until_bound;
b o o l result;
i f (!StateSet->subf_cache->try_to_evaluate(&s_p_h_f, result))
{
state *nextstate;
curstate = s;
StateCopy(workingstate, curstate);
i f (verify_bpmc_rec(curstate, f->subf2))
fin_prob = 1.0;
e l s e i f (f->until_bound == 0 ||
!verify_bpmc_rec(curstate, f->subf1))
fin_prob = 0.0;
else
{
/* level is a long because it refers to the unsigned
f->until_bound, but may be -1 */
l o n g level = 0;
double nextprob, globreachprob = 1.0, fin_prob_loc = 0.0;
b o o l tmp_psi, tmp_phi;
StateSet->IncrNumElts();
StateSet->QueuePush(s, 1.0, 0.0);
w h i l e (level >= 0)
{
double dummy;
curstate = StateSet->QueueTop(dummy);
StateCopy(workingstate, curstate);
i f (args->ctrl_next.value)
( v o i d *)Rules->EnabledTransition();
nextstate = Rules->SeqNextState(nextprob);
Reporter->print_progress(level);
180 Chapter B. FHP-Murϕ: the Verifier

/* assumption: it is effectively a Markov Chain */


i f (nextstate != NULL)
{
s_p_h_f._state = *nextstate;
s_p_h_f._horizon = f->until_bound - level - 1;
s_p_h_f._subformula = f;
s_p_h_f._probability =
StateSet->subf_cache->simple_was_present(&s_p_h_f);
level++;
i f (s_p_h_f._probability == -1.0)
{
i f (!(tmp_psi = verify_bpmc_rec(nextstate, f->subf2)) &&
level != f->until_bound)
tmp_phi = verify_bpmc_rec(nextstate, f->subf1);
StateSet->QueuePush(nextstate, nextprob, tmp_psi? 1.0 : 0.0);
}
else
StateSet->QueuePush(nextstate, nextprob,
s_p_h_f._probability);
/* Alternatively, don’t push and don’t pop below... */
globreachprob *= nextprob;
}
i f (nextstate == NULL || s_p_h_f._probability != -1.0 ||
level == f->until_bound || tmp_psi || !tmp_phi)
{
double formprob, reachprob;
--level;
curstate = StateSet->QueueDequeue(reachprob, formprob);
fin_prob_loc += update_prob_loc(tmp_psi, globreachprob,
s_p_h_f._probability);
globreachprob /= reachprob;
i f (level != -1)
{
fin_prob = StateSet->QueueUpdateProb(reachprob*formprob);
i f (may_exit(f, fin_prob_loc, level))
{
curstate = StateSet->ResetQueue(level);
StateCopy(workingstate, curstate);/* to restore nextstate */
fin_prob = fin_prob_loc;
r e t u r n (f->ord == PCTL_G || f->ord == PCTL_GEQ);
}
}
s_p_h_f._probability = formprob;
s_p_h_f._state = *curstate;
s_p_h_f._horizon = f->until_bound - level - 1;
s_p_h_f._subformula = f;
i f (!StateSet->subf_cache->insert(&s_p_h_f))
printf("WARNING: Too small cache size, computation may requi\
re a lot of time\n");
tmp_psi = f a l s e ; s_p_h_f._probability = -1.0;
}
else
StateSet->IncrNumElts();
} // while
fin_prob = StateSet->QueueFinalProb();
i f (args->verbose.value > 1)
printf("\tThe probability of the formula is %.10lg\n",
fin_prob);
} // else
s w i t c h (f->ord)
{
c a s e PCTL_L : r e t u r n fin_prob < f->prob_bound;
B.1 C++ Code 181

c a s e PCTL_LEQ : r e t u r n fin_prob <= f->prob_bound;


c a s e PCTL_G : r e t u r n fin_prob > f->prob_bound;
c a s e PCTL_GEQ : r e t u r n fin_prob >= f->prob_bound;
d e f a u l t : Error.Notrace("Internal error: invalid value %d for\
f->ord", f->ord); r e t u r n FALSE;
}
} // try_to_evaluate failed
e l s e r e t u r n result; // try_to_evaluate succeeded
}
/****************************************
The simulation main routine:
void simulate()
****************************************/
// Uli: added required call to theworld.to_state()
void
AlgorithmManager::simulate()
{
// progress report must be printed out so as to make sense
// otherwise, if there is no bug, the program just run on for ever
// without any message.
// print verbose message
i f (args->verbose.value) Reporter->print_verbose_header();
Reporter->StartSimulation();
theworld.to_state(NULL); // trick: marks variables in world
// GetRandomStartState will choose a Startstate randomly
curstate = StartState->RandomStartState();
// simulate
w h i l e (1)
curstate = Rules->RandomNextState();
}
A PPENDIX C

PRISM CODE FOR LQS WITH 10 ENTRIES

In the following we report the PRISM code for the LQS with 10 entries (see Sections 5.4.1 and 5.4.2).
probabilistic
c o n s t ITEM_Q = 10;
c o n s t ITEM_Q_m_1 = 9;
−x
//exp_f_x_d_y means 1−ey
r a t e exp_f_1_d_4 = 0.15803013970713941960;
r a t e exp_f_2_d_4 = 0.21616617919084682702;
r a t e exp_f_1_d_5 = 0.12642411176571153568;
r a t e exp_f_2_d_5 = 0.17293294335267746162;
r a t e exp_f_3_d_5 = 0.19004258632642721140;
r a t e exp_f_1_d_6 = 0.10535342647142627973;
r a t e exp_f_2_d_6 = 0.14411078612723121801;
r a t e exp_f_3_d_6 = 0.15836882193868934283;
r a t e exp_f_4_d_6 = 0.16361406018521096995;
r a t e exp_f_1_d_7 = 0.09030293697550823977;
r a t e exp_f_2_d_7 = 0.12352353096619818687;
r a t e exp_f_3_d_7 = 0.13574470451887657957;
r a t e exp_f_4_d_7 = 0.14024062301589511710;
r a t e exp_f_5_d_7 = 0.14189457900013064755;
r a t e exp_f_1_d_8 = 0.07901506985356970980;
r a t e exp_f_2_d_8 = 0.10808308959542341351;
r a t e exp_f_3_d_8 = 0.11877661645401700712;
r a t e exp_f_4_d_8 = 0.12271054513890822746;
r a t e exp_f_5_d_8 = 0.12415775662511431661;
r a t e exp_f_1_d_9 = 0.07023561764761751982;
r a t e exp_f_2_d_9 = 0.09607385741815414534;
r a t e exp_f_3_d_9 = 0.10557921462579289522;
r a t e exp_f_4_d_9 = 0.10907604012347397996;
r a t e exp_f_5_d_9 = 0.11036245033343494810;
r a t e exp_f_6_d_9 = 0.11083569420259262684;
r a t e exp_f_1_d_10 = 0.06321205588285576784;
r a t e exp_f_2_d_10 = 0.08646647167633873081;
r a t e exp_f_3_d_10 = 0.09502129316321360570;
r a t e exp_f_4_d_10 = 0.09816843611112658197;
r a t e exp_f_5_d_10 = 0.09932620530009145329;
r a t e exp_f_6_d_10 = 0.09975212478233336415;
r a t e exp_f_7_d_10 = 0.09990881180344454838;
r a t e exp_f_8_d_10 = 0.09996645373720974881;
r a t e a_third = 1/3;
r a t e a_fourth = 0.25;
r a t e a_fifth = 0.20;
r a t e a_sixth = 1/6;
r a t e a_seventh = 1/7;
r a t e a_eigth = 1/8;
r a t e a_nineth = 1/9;
r a t e a_tenth = 1/10;
//These constants are calculated to have the sum of probabilities
// on exiting transitions equal to 1
r a t e rem_1 = 0.34196986029286058040;
r a t e rem_2 = 0.30064294488161100270;
r a t e rem_3 = 0.31060035855518379130;

183
184 Chapter C. PRISM code for LQS with 10 entries

rate rem_4 = 0.26188623861077552282;


rate rem_5 = 0.19725692233296732550;
rate rem_6 = 0.17561490342671166250;
rate rem_7 = 0.15814460128059594786;
rate rem_8 = 0.15817814754338619905;
module main
q0 : [0..1]; // 0 : noerr; 1 : err
q1 : [0..1];
q2 : [0..1];
q3 : [0..1];
q4 : [0..1];
q5 : [0..1];
q6 : [0..1];
q7 : [0..1];
q8 : [0..1];
q9 : [0..1];
num : [0..ITEM_Q_m_1];// number of items in the queue
h : [0..ITEM_Q_m_1];// head of the queue
// 2 moves (enqueue and nomove)
[] (num = 0) -> 0.5 : (num’ = num+1) + 0.5 : (num’ = num);
// 3 moves (enqueue, dequeue, nomove)
[] (num = 1 & h = ITEM_Q-1) -> a_third : (num’ = num+1) +
a_third : (num’ = num-1 & h’ = 0) +
a_third : (num’ = num);
[] (num = 1 & h != ITEM_Q-1) ->
a_third : (num’ = num+1) +
a_third : (num’ = num-1 & h’ = h+1) +
a_third : (num’ = num);
// 4 moves
// enqueue, dequeue, 1 item status change, nomove
[] (num = 2 & h = 0) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 1) +
exp_f_1_d_4 : (q1’ = 1) +
rem_1 : (num’ = num);
[] (num = 2 & h = 1) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 2) +
exp_f_1_d_4 : (q2’ = 1) +
rem_1 : (num’ = num);
[] (num = 2 & h = 2) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_4 : (q3’ = 1) +
rem_1 : (num’ = num);
[] (num = 2 & h = 3) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_4 : (q4’ = 1) +
rem_1 : (num’ = num);
[] (num = 2 & h = 4) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_4 : (q5’ = 1) +
rem_1 : (num’ = num);
[] (num = 2 & h = 5) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_4 : (q6’ = 1) +
rem_1 : (num’ = num);
[] (num = 2 & h = 6) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_4 : (q7’ = 1) +
rem_1 : (num’ = num);
[] (num = 2 & h = 7) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_4 : (q8’ = 1) +
rem_1 : (num’ = num);
[] (num = 2 & h = 8) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_4 : (q9’ = 1) +
185

rem_1 : (num’ = num);


[] (num = 2 & h = 9) -> a_fourth : (num’ = num+1) +
a_fourth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_4 : (q0’ = 1) +
rem_1 : (num’ = num);
// 5 moves
// enqueue, dequeue, 2 item status change, nomove
[] (num = 3 & h = 0) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 1) +
exp_f_1_d_5 : (q1’ = 1) +
exp_f_2_d_5 : (q2’ = 1) +
rem_2 : (num’ = num);
[] (num = 3 & h = 1) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 2) +
exp_f_1_d_5 : (q2’ = 1) +
exp_f_2_d_5 : (q3’ = 1) +
rem_2 : (num’ = num);
[] (num = 3 & h = 2) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_5 : (q3’ = 1) +
exp_f_2_d_5 : (q4’ = 1) +
rem_2 : (num’ = num);
[] (num = 3 & h = 3) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_5 : (q4’ = 1) +
exp_f_2_d_5 : (q5’ = 1) +
rem_2 : (num’ = num);
[] (num = 3 & h = 4) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_5 : (q5’ = 1) +
exp_f_2_d_5 : (q6’ = 1) +
rem_2 : (num’ = num);
[] (num = 3 & h = 5) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_5 : (q6’ = 1) +
exp_f_2_d_5 : (q7’ = 1) +
rem_2 : (num’ = num);
[] (num = 3 & h = 6) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_5 : (q7’ = 1) +
exp_f_2_d_5 : (q8’ = 1) +
rem_2 : (num’ = num);
[] (num = 3 & h = 7) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_5 : (q8’ = 1) +
exp_f_2_d_5 : (q9’ = 1) +
rem_2 : (num’ = num);
[] (num = 3 & h = 8) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_5 : (q9’ = 1) +
exp_f_2_d_5 : (q0’ = 1) +
rem_2 : (num’ = num);
[] (num = 3 & h = 9) -> a_fifth : (num’ = num+1) +
a_fifth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_5 : (q0’ = 1) +
exp_f_2_d_5 : (q1’ = 1) +
rem_2 : (num’ = num);
// 6 moves
// enqueue, dequeue, 3 item status change, nomove
[] (num = 4 & h = 0) -> a_sixth : (num’ = num+1) +
a_sixth : (num’ = num-1 & h’ = 1) +
exp_f_1_d_6 : (q1’ = 1) +
exp_f_2_d_6 : (q2’ = 1) +
exp_f_3_d_6 : (q3’ = 1) +
rem_3 : (num’ = num);
[] (num = 4 & h = 1) -> a_sixth : (num’ = num+1) +
186 Chapter C. PRISM code for LQS with 10 entries

a_sixth : (num’ = num-1 & h’ = 2) +


exp_f_1_d_6 : (q2’ = 1) +
exp_f_2_d_6 : (q3’ = 1) +
exp_f_3_d_6 : (q4’ = 1) +
rem_3 : (num’ = num);
[] (num = 4 & h = 2) -> a_sixth : (num’ = num+1) +
a_sixth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_6 : (q3’ = 1) +
exp_f_2_d_6 : (q4’ = 1) +
exp_f_3_d_6 : (q5’ = 1) +
rem_3 : (num’ = num);
[] (num = 4 & h = 3) -> a_sixth : (num’ = num+1) +
a_sixth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_6 : (q4’ = 1) +
exp_f_2_d_6 : (q5’ = 1) +
exp_f_3_d_6 : (q6’ = 1) +
rem_3 : (num’ = num);
[] (num = 4 & h = 4) -> a_sixth : (num’ = num+1) +
a_sixth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_6 : (q5’ = 1) +
exp_f_2_d_6 : (q6’ = 1) +
exp_f_3_d_6 : (q7’ = 1) +
rem_3 : (num’ = num);
[] (num = 4 & h = 5) -> a_sixth : (num’ = num+1) +
a_sixth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_6 : (q6’ = 1) +
exp_f_2_d_6 : (q7’ = 1) +
exp_f_3_d_6 : (q8’ = 1) +
rem_3 : (num’ = num);
[] (num = 4 & h = 6) -> a_sixth : (num’ = num+1) +
a_sixth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_6 : (q7’ = 1) +
exp_f_2_d_6 : (q8’ = 1) +
exp_f_3_d_6 : (q9’ = 1) +
rem_3 : (num’ = num);
[] (num = 4 & h = 7) -> a_sixth : (num’ = num+1) +
a_sixth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_6 : (q8’ = 1) +
exp_f_2_d_6 : (q9’ = 1) +
exp_f_3_d_6 : (q0’ = 1) +
rem_3 : (num’ = num);
[] (num = 4 & h = 8) -> a_sixth : (num’ = num+1) +
a_sixth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_6 : (q9’ = 1) +
exp_f_2_d_6 : (q0’ = 1) +
exp_f_3_d_6 : (q1’ = 1) +
rem_3 : (num’ = num);
[] (num = 4 & h = 9) -> a_sixth : (num’ = num+1) +
a_sixth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_6 : (q0’ = 1) +
exp_f_2_d_6 : (q1’ = 1) +
exp_f_3_d_6 : (q2’ = 1) +
rem_3 : (num’ = num);
// 7 moves
// enqueue, dequeue, 4 item status change, nomove
[] (num = 5 & h = 0) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 1) +
exp_f_1_d_7 : (q1’ = 1) +
exp_f_2_d_7 : (q2’ = 1) +
exp_f_3_d_7 : (q3’ = 1) +
exp_f_4_d_7 : (q4’ = 1) +
rem_4 : (num’ = num);
[] (num = 5 & h = 1) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 2) +
exp_f_1_d_7 : (q2’ = 1) +
exp_f_2_d_7 : (q3’ = 1) +
exp_f_3_d_7 : (q4’ = 1) +
187

exp_f_4_d_7 : (q5’ = 1) +
rem_4 : (num’ = num);
[] (num = 5 & h = 2) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 3) +
exp_f_1_d_7 : (q3’ = 1) +
exp_f_2_d_7 : (q4’ = 1) +
exp_f_3_d_7 : (q5’ = 1) +
exp_f_4_d_7 : (q6’ = 1) +
rem_4 : (num’ = num);
[] (num = 5 & h = 3) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 4) +
exp_f_1_d_7 : (q4’ = 1) +
exp_f_2_d_7 : (q5’ = 1) +
exp_f_3_d_7 : (q6’ = 1) +
exp_f_4_d_7 : (q7’ = 1) +
rem_4 : (num’ = num);
[] (num = 5 & h = 4) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 5) +
exp_f_1_d_7 : (q5’ = 1) +
exp_f_2_d_7 : (q6’ = 1) +
exp_f_3_d_7 : (q7’ = 1) +
exp_f_4_d_7 : (q8’ = 1) +
rem_4 : (num’ = num);
[] (num = 5 & h = 5) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 0) +
exp_f_1_d_7 : (q6’ = 1) +
exp_f_2_d_7 : (q7’ = 1) +
exp_f_3_d_7 : (q8’ = 1) +
exp_f_4_d_7 : (q9’ = 1) +
rem_4 : (num’ = num);
[] (num = 5 & h = 6) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 3) +
exp_f_1_d_7 : (q7’ = 1) +
exp_f_2_d_7 : (q8’ = 1) +
exp_f_3_d_7 : (q9’ = 1) +
exp_f_4_d_7 : (q0’ = 1) +
rem_4 : (num’ = num);
[] (num = 5 & h = 7) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 4) +
exp_f_1_d_7 : (q8’ = 1) +
exp_f_2_d_7 : (q9’ = 1) +
exp_f_3_d_7 : (q0’ = 1) +
exp_f_4_d_7 : (q1’ = 1) +
rem_4 : (num’ = num);
[] (num = 5 & h = 8) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 5) +
exp_f_1_d_7 : (q9’ = 1) +
exp_f_2_d_7 : (q0’ = 1) +
exp_f_3_d_7 : (q1’ = 1) +
exp_f_4_d_7 : (q2’ = 1) +
rem_4 : (num’ = num);
[] (num = 5 & h = 9) -> a_seventh : (num’ = num+1) +
a_seventh : (num’ = num-1 & h’ = 0) +
exp_f_1_d_7 : (q0’ = 1) +
exp_f_2_d_7 : (q1’ = 1) +
exp_f_3_d_7 : (q2’ = 1) +
exp_f_4_d_7 : (q3’ = 1) +
rem_4 : (num’ = num);
// 8 moves
// enqueue, dequeue, 5 item status change, nomove
[] (num = 6 & h = 0) -> a_eigth : (num’ = num+1) +
a_eigth : (num’ = num-1 & h’ = 1) +
exp_f_1_d_8 : (q1’ = 1) +
exp_f_2_d_8 : (q2’ = 1) +
exp_f_3_d_8 : (q3’ = 1) +
exp_f_4_d_8 : (q4’ = 1) +
exp_f_5_d_8 : (q5’ = 1) +
rem_5 : (num’ = num);
188 Chapter C. PRISM code for LQS with 10 entries

[] (num = 6 & h = 1) -> a_eigth : (num’ = num+1) +


a_eigth : (num’ = num-1 & h’ = 2) +
exp_f_1_d_8 : (q2’ = 1) +
exp_f_2_d_8 : (q3’ = 1) +
exp_f_3_d_8 : (q4’ = 1) +
exp_f_4_d_8 : (q5’ = 1) +
exp_f_5_d_8 : (q6’ = 1) +
rem_5 : (num’ = num);
[] (num = 6 & h = 2) -> a_eigth : (num’ = num+1) +
a_eigth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_8 : (q3’ = 1) +
exp_f_2_d_8 : (q4’ = 1) +
exp_f_3_d_8 : (q5’ = 1) +
exp_f_4_d_8 : (q6’ = 1) +
exp_f_5_d_8 : (q7’ = 1) +
rem_5 : (num’ = num);
[] (num = 6 & h = 3) -> a_eigth : (num’ = num+1) +
a_eigth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_8 : (q4’ = 1) +
exp_f_2_d_8 : (q5’ = 1) +
exp_f_3_d_8 : (q6’ = 1) +
exp_f_4_d_8 : (q7’ = 1) +
exp_f_5_d_8 : (q8’ = 1) +
rem_5 : (num’ = num);
[] (num = 6 & h = 4) -> a_eigth : (num’ = num+1) +
a_eigth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_8 : (q5’ = 1) +
exp_f_2_d_8 : (q6’ = 1) +
exp_f_3_d_8 : (q7’ = 1) +
exp_f_4_d_8 : (q8’ = 1) +
exp_f_5_d_8 : (q9’ = 1) +
rem_5 : (num’ = num);
[] (num = 6 & h = 5) -> a_eigth : (num’ = num+1) +
a_eigth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_8 : (q6’ = 1) +
exp_f_2_d_8 : (q7’ = 1) +
exp_f_3_d_8 : (q8’ = 1) +
exp_f_4_d_8 : (q9’ = 1) +
exp_f_5_d_8 : (q0’ = 1) +
rem_5 : (num’ = num);
[] (num = 6 & h = 6) -> a_eigth : (num’ = num+1) +
a_eigth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_8 : (q7’ = 1) +
exp_f_2_d_8 : (q8’ = 1) +
exp_f_3_d_8 : (q9’ = 1) +
exp_f_4_d_8 : (q0’ = 1) +
exp_f_5_d_8 : (q1’ = 1) +
rem_5 : (num’ = num);
[] (num = 6 & h = 7) -> a_eigth : (num’ = num+1) +
a_eigth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_8 : (q8’ = 1) +
exp_f_2_d_8 : (q9’ = 1) +
exp_f_3_d_8 : (q0’ = 1) +
exp_f_4_d_8 : (q1’ = 1) +
exp_f_5_d_8 : (q2’ = 1) +
rem_5 : (num’ = num);
[] (num = 6 & h = 8) -> a_eigth : (num’ = num+1) +
a_eigth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_8 : (q9’ = 1) +
exp_f_2_d_8 : (q0’ = 1) +
exp_f_3_d_8 : (q1’ = 1) +
exp_f_4_d_8 : (q2’ = 1) +
exp_f_5_d_8 : (q3’ = 1) +
rem_5 : (num’ = num);
[] (num = 6 & h = 9) -> a_eigth : (num’ = num+1) +
a_eigth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_8 : (q0’ = 1) +
exp_f_2_d_8 : (q1’ = 1) +
189

exp_f_3_d_8 : (q2’ = 1) +
exp_f_4_d_8 : (q3’ = 1) +
exp_f_5_d_8 : (q4’ = 1) +
rem_5 : (num’ = num);
// 9 moves
// enqueue, dequeue, 6 item status change, nomove
[] (num = 7 & h = 0) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 1) +
exp_f_1_d_9 : (q1’ = 1) +
exp_f_2_d_9 : (q2’ = 1) +
exp_f_3_d_9 : (q3’ = 1) +
exp_f_4_d_9 : (q4’ = 1) +
exp_f_5_d_9 : (q5’ = 1) +
exp_f_6_d_9 : (q6’ = 1) +
rem_6 : (num’ = num);
[] (num = 7 & h = 1) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 2) +
exp_f_1_d_9 : (q2’ = 1) +
exp_f_2_d_9 : (q3’ = 1) +
exp_f_3_d_9 : (q4’ = 1) +
exp_f_4_d_9 : (q5’ = 1) +
exp_f_5_d_9 : (q6’ = 1) +
exp_f_6_d_9 : (q7’ = 1) +
rem_6 : (num’ = num);
[] (num = 7 & h = 2) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_9 : (q3’ = 1) +
exp_f_2_d_9 : (q4’ = 1) +
exp_f_3_d_9 : (q5’ = 1) +
exp_f_4_d_9 : (q6’ = 1) +
exp_f_5_d_9 : (q7’ = 1) +
exp_f_6_d_9 : (q8’ = 1) +
rem_6 : (num’ = num);
[] (num = 7 & h = 3) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_9 : (q4’ = 1) +
exp_f_2_d_9 : (q5’ = 1) +
exp_f_3_d_9 : (q6’ = 1) +
exp_f_4_d_9 : (q7’ = 1) +
exp_f_5_d_9 : (q8’ = 1) +
exp_f_6_d_9 : (q9’ = 1) +
rem_6 : (num’ = num);
[] (num = 7 & h = 4) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_9 : (q5’ = 1) +
exp_f_2_d_9 : (q6’ = 1) +
exp_f_3_d_9 : (q7’ = 1) +
exp_f_4_d_9 : (q8’ = 1) +
exp_f_5_d_9 : (q9’ = 1) +
exp_f_6_d_9 : (q0’ = 1) +
rem_6 : (num’ = num);
[] (num = 7 & h = 5) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_9 : (q6’ = 1) +
exp_f_2_d_9 : (q7’ = 1) +
exp_f_3_d_9 : (q8’ = 1) +
exp_f_4_d_9 : (q9’ = 1) +
exp_f_5_d_9 : (q0’ = 1) +
exp_f_6_d_9 : (q1’ = 1) +
rem_6 : (num’ = num);
[] (num = 7 & h = 6) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_9 : (q7’ = 1) +
exp_f_2_d_9 : (q8’ = 1) +
exp_f_3_d_9 : (q9’ = 1) +
exp_f_4_d_9 : (q0’ = 1) +
exp_f_5_d_9 : (q1’ = 1) +
190 Chapter C. PRISM code for LQS with 10 entries

exp_f_6_d_9 : (q2’ = 1) +
rem_6 : (num’ = num);
[] (num = 7 & h = 7) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_9 : (q8’ = 1) +
exp_f_2_d_9 : (q9’ = 1) +
exp_f_3_d_9 : (q0’ = 1) +
exp_f_4_d_9 : (q1’ = 1) +
exp_f_5_d_9 : (q2’ = 1) +
exp_f_6_d_9 : (q3’ = 1) +
rem_6 : (num’ = num);
[] (num = 7 & h = 8) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_9 : (q9’ = 1) +
exp_f_2_d_9 : (q0’ = 1) +
exp_f_3_d_9 : (q1’ = 1) +
exp_f_4_d_9 : (q2’ = 1) +
exp_f_5_d_9 : (q3’ = 1) +
exp_f_6_d_9 : (q4’ = 1) +
rem_6 : (num’ = num);
[] (num = 7 & h = 9) -> a_nineth : (num’ = num+1) +
a_nineth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_9 : (q0’ = 1) +
exp_f_2_d_9 : (q1’ = 1) +
exp_f_3_d_9 : (q2’ = 1) +
exp_f_4_d_9 : (q3’ = 1) +
exp_f_5_d_9 : (q4’ = 1) +
exp_f_6_d_9 : (q5’ = 1) +
rem_6 : (num’ = num);
// 10 moves
// enqueue, dequeue, 7 item status change, nomove
[] (num = 8 & h = 0) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 1) +
exp_f_1_d_10 : (q1’ = 1) +
exp_f_2_d_10 : (q2’ = 1) +
exp_f_3_d_10 : (q3’ = 1) +
exp_f_4_d_10 : (q4’ = 1) +
exp_f_5_d_10 : (q5’ = 1) +
exp_f_6_d_10 : (q6’ = 1) +
exp_f_7_d_10 : (q7’ = 1) +
rem_7 : (num’ = num);
[] (num = 8 & h = 1) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 2) +
exp_f_1_d_10 : (q2’ = 1) +
exp_f_2_d_10 : (q3’ = 1) +
exp_f_3_d_10 : (q4’ = 1) +
exp_f_4_d_10 : (q5’ = 1) +
exp_f_5_d_10 : (q6’ = 1) +
exp_f_6_d_10 : (q7’ = 1) +
exp_f_7_d_10 : (q8’ = 1) +
rem_7 : (num’ = num);
[] (num = 8 & h = 2) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_10 : (q3’ = 1) +
exp_f_2_d_10 : (q4’ = 1) +
exp_f_3_d_10 : (q5’ = 1) +
exp_f_4_d_10 : (q6’ = 1) +
exp_f_5_d_10 : (q7’ = 1) +
exp_f_6_d_10 : (q8’ = 1) +
exp_f_7_d_10 : (q9’ = 1) +
rem_7 : (num’ = num);
[] (num = 8 & h = 3) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_10 : (q4’ = 1) +
exp_f_2_d_10 : (q5’ = 1) +
exp_f_3_d_10 : (q6’ = 1) +
exp_f_4_d_10 : (q7’ = 1) +
exp_f_5_d_10 : (q8’ = 1) +
191

exp_f_6_d_10 : (q9’ = 1) +
exp_f_7_d_10 : (q0’ = 1) +
rem_7 : (num’ = num);
[] (num = 8 & h = 4) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_10 : (q5’ = 1) +
exp_f_2_d_10 : (q6’ = 1) +
exp_f_3_d_10 : (q7’ = 1) +
exp_f_4_d_10 : (q8’ = 1) +
exp_f_5_d_10 : (q9’ = 1) +
exp_f_6_d_10 : (q0’ = 1) +
exp_f_7_d_10 : (q1’ = 1) +
rem_7 : (num’ = num);
[] (num = 8 & h = 5) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_10 : (q6’ = 1) +
exp_f_2_d_10 : (q7’ = 1) +
exp_f_3_d_10 : (q8’ = 1) +
exp_f_4_d_10 : (q9’ = 1) +
exp_f_5_d_10 : (q0’ = 1) +
exp_f_6_d_10 : (q1’ = 1) +
exp_f_7_d_10 : (q2’ = 1) +
rem_7 : (num’ = num);
[] (num = 8 & h = 6) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_10 : (q7’ = 1) +
exp_f_2_d_10 : (q8’ = 1) +
exp_f_3_d_10 : (q9’ = 1) +
exp_f_4_d_10 : (q0’ = 1) +
exp_f_5_d_10 : (q1’ = 1) +
exp_f_6_d_10 : (q2’ = 1) +
exp_f_7_d_10 : (q3’ = 1) +
rem_7 : (num’ = num);
[] (num = 8 & h = 7) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_10 : (q8’ = 1) +
exp_f_2_d_10 : (q9’ = 1) +
exp_f_3_d_10 : (q0’ = 1) +
exp_f_4_d_10 : (q1’ = 1) +
exp_f_5_d_10 : (q2’ = 1) +
exp_f_6_d_10 : (q3’ = 1) +
exp_f_7_d_10 : (q4’ = 1) +
rem_7 : (num’ = num);
[] (num = 8 & h = 8) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_10 : (q9’ = 1) +
exp_f_2_d_10 : (q0’ = 1) +
exp_f_3_d_10 : (q1’ = 1) +
exp_f_4_d_10 : (q2’ = 1) +
exp_f_5_d_10 : (q3’ = 1) +
exp_f_6_d_10 : (q4’ = 1) +
exp_f_7_d_10 : (q5’ = 1) +
rem_7 : (num’ = num);
[] (num = 8 & h = 9) -> a_tenth : (num’ = num+1) +
a_tenth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_10 : (q0’ = 1) +
exp_f_2_d_10 : (q1’ = 1) +
exp_f_3_d_10 : (q2’ = 1) +
exp_f_4_d_10 : (q3’ = 1) +
exp_f_5_d_10 : (q4’ = 1) +
exp_f_6_d_10 : (q5’ = 1) +
exp_f_7_d_10 : (q6’ = 1) +
rem_7 : (num’ = num);
// dequeue, 8 items status change, nomove
[] (num = 9 & h = 0) -> a_tenth : (num’ = num-1 & h’ = 1) +
exp_f_1_d_10 : (q1’ = 1) +
exp_f_2_d_10 : (q2’ = 1) +
exp_f_3_d_10 : (q3’ = 1) +
exp_f_4_d_10 : (q4’ = 1) +
192 Chapter C. PRISM code for LQS with 10 entries

exp_f_5_d_10 : (q5’ = 1) +
exp_f_6_d_10 : (q6’ = 1) +
exp_f_7_d_10 : (q7’ = 1) +
exp_f_8_d_10 : (q8’ = 1) +
rem_8 : (num’ = num);
[] (num = 9 & h = 1) -> a_tenth : (num’ = num-1 & h’ = 2) +
exp_f_1_d_10 : (q2’ = 1) +
exp_f_2_d_10 : (q3’ = 1) +
exp_f_3_d_10 : (q4’ = 1) +
exp_f_4_d_10 : (q5’ = 1) +
exp_f_5_d_10 : (q6’ = 1) +
exp_f_6_d_10 : (q7’ = 1) +
exp_f_7_d_10 : (q8’ = 1) +
exp_f_8_d_10 : (q9’ = 1) +
rem_8 : (num’ = num);
[] (num = 9 & h = 2) -> a_tenth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_10 : (q3’ = 1) +
exp_f_2_d_10 : (q4’ = 1) +
exp_f_3_d_10 : (q5’ = 1) +
exp_f_4_d_10 : (q6’ = 1) +
exp_f_5_d_10 : (q7’ = 1) +
exp_f_6_d_10 : (q8’ = 1) +
exp_f_7_d_10 : (q9’ = 1) +
exp_f_8_d_10 : (q0’ = 1) +
rem_8 : (num’ = num);
[] (num = 9 & h = 3) -> a_tenth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_10 : (q4’ = 1) +
exp_f_2_d_10 : (q5’ = 1) +
exp_f_3_d_10 : (q6’ = 1) +
exp_f_4_d_10 : (q7’ = 1) +
exp_f_5_d_10 : (q8’ = 1) +
exp_f_6_d_10 : (q9’ = 1) +
exp_f_7_d_10 : (q0’ = 1) +
exp_f_8_d_10 : (q1’ = 1) +
rem_8 : (num’ = num);
[] (num = 9 & h = 4) -> a_tenth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_10 : (q5’ = 1) +
exp_f_2_d_10 : (q6’ = 1) +
exp_f_3_d_10 : (q7’ = 1) +
exp_f_4_d_10 : (q8’ = 1) +
exp_f_5_d_10 : (q9’ = 1) +
exp_f_6_d_10 : (q0’ = 1) +
exp_f_7_d_10 : (q1’ = 1) +
exp_f_8_d_10 : (q2’ = 1) +
rem_8 : (num’ = num);
[] (num = 9 & h = 5) -> a_tenth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_10 : (q6’ = 1) +
exp_f_2_d_10 : (q7’ = 1) +
exp_f_3_d_10 : (q8’ = 1) +
exp_f_4_d_10 : (q9’ = 1) +
exp_f_5_d_10 : (q0’ = 1) +
exp_f_6_d_10 : (q1’ = 1) +
exp_f_7_d_10 : (q2’ = 1) +
exp_f_8_d_10 : (q3’ = 1) +
rem_8 : (num’ = num);
[] (num = 9 & h = 6) -> a_tenth : (num’ = num-1 & h’ = 3) +
exp_f_1_d_10 : (q7’ = 1) +
exp_f_2_d_10 : (q8’ = 1) +
exp_f_3_d_10 : (q9’ = 1) +
exp_f_4_d_10 : (q0’ = 1) +
exp_f_5_d_10 : (q1’ = 1) +
exp_f_6_d_10 : (q2’ = 1) +
exp_f_7_d_10 : (q3’ = 1) +
exp_f_8_d_10 : (q4’ = 1) +
rem_8 : (num’ = num);
[] (num = 9 & h = 7) -> a_tenth : (num’ = num-1 & h’ = 4) +
exp_f_1_d_10 : (q8’ = 1) +
exp_f_2_d_10 : (q9’ = 1) +
exp_f_3_d_10 : (q0’ = 1) +
193

exp_f_4_d_10 : (q1’ = 1) +
exp_f_5_d_10 : (q2’ = 1) +
exp_f_6_d_10 : (q3’ = 1) +
exp_f_7_d_10 : (q4’ = 1) +
exp_f_8_d_10 : (q5’ = 1) +
rem_8 : (num’ = num);
[] (num = 9 & h = 8) -> a_tenth : (num’ = num-1 & h’ = 5) +
exp_f_1_d_10 : (q9’ = 1) +
exp_f_2_d_10 : (q0’ = 1) +
exp_f_3_d_10 : (q1’ = 1) +
exp_f_4_d_10 : (q2’ = 1) +
exp_f_5_d_10 : (q3’ = 1) +
exp_f_6_d_10 : (q4’ = 1) +
exp_f_7_d_10 : (q5’ = 1) +
exp_f_8_d_10 : (q6’ = 1) +
rem_8 : (num’ = num);
[] (num = 9 & h = 9) -> a_tenth : (num’ = num-1 & h’ = 0) +
exp_f_1_d_10 : (q0’ = 1) +
exp_f_2_d_10 : (q1’ = 1) +
exp_f_3_d_10 : (q2’ = 1) +
exp_f_4_d_10 : (q3’ = 1) +
exp_f_5_d_10 : (q4’ = 1) +
exp_f_6_d_10 : (q5’ = 1) +
exp_f_7_d_10 : (q6’ = 1) +
exp_f_8_d_10 : (q7’ = 1) +
rem_8 : (num’ = num);
endmodule
194 Chapter C. PRISM code for LQS with 10 entries
R EFERENCES

[1] A ZIZ , A., S ANWAL , K., S INGHAL , V., AND B RAYTON , R. Model-checking continuous-time markov
chains. ACM Trans. Comput. Logic 1, 1 (2000), 162–170.

[2] BAIER , C., C LARKE , E. M., H ARTONAS -G ARMHAUSEN , V., K WIATKOWSKA , M., AND RYAN ,
M. Symbolic model checking for probabilistic processes. In Automata, Languages and Program-
ming, 24th International Colloquium, ICALP’97, Bologna, Italy, 7-11 July 1997, Proceedings (1997),
P. Degano, R. Gorrieri, and A. Marchetti-Spaccamela, Eds., vol. 1256 of Lecture Notes in Computer
Science, Springer, pp. 430–440.

[3] B EHRENDS , E. Introduction to Markov Chains. Vieweg, 2000.

[4] B IANCO , AND DE A LFARO. Model checking of probabilistic and nondeterministic systems. In Foun-
dations of Software Technology and Theoretical Computer Science, 15th Conference, Bangalore, In-
dia, December 18-20, 1995, Proceedings (1995), P. S. Thiagarajan, Ed., vol. 1026 of Lecture Notes in
Computer Science, Springer, pp. 499–513.

[5] B RYANT, R. Graph-based algorithms for boolean function manipulation. IEEE Trans. on Computers
C-35, 8 (Aug 1986), 677–691.

[6] B URCH , J. R., C LARKE , E. M., M C M ILLAN , K. L., D ILL , D. L., AND H WANG , L. J. Symbolic
model checking: 1020 states and beyond. Inf. Comput. 98, 2 (1992), 142–170.

[7] C. BAIER , J. K., AND H ERMANNS , H. Approximate symbolic model checking of continuous-time
markov chains. In CONCUR ’99: Concurrency Theory, 10th International Conference, Eindhoven,
The Netherlands, August 24-27, 1999, Proceedings (1999), J. C. M. Baeten and S. Mauw, Eds.,
vol. 1664 of Lecture Notes in Computer Science, Springer, pp. 146–161.

[8] C IARDO , G., AND T ILGNER , M. On the use of Kronecker operators for the solution of general-
ized stocastic Petri nets. ICASE Report 96-35, Institute for Computer Applications in Science and
Engineering, 1996.

[9] C IARDO , G., AND T RIVEDI , K. A decomposition approach for stochastic reward net models. Per-
formance Evaluation 18, 1 (1993), 37–59.

[10] C LARKE , E. M., E MERSON , E. A., AND S ISTLA , A. P. Automatic verification of finite-state con-
current systems using temporal logic specifications. ACM Trans. Program. Lang. Syst. 8, 2 (1986),
244–263.

[11] C LARKE , E. M., G RUMBERG , O., AND P ELED , D. A. Model Checking. The MIT Press, 1999.

[12] C LARKE , E. M., M C M ILLAN , K. L., Z HAO , X., F UJITA , M., AND YANG , J. Spectral transforms
for large boolean functions with applications to technology mapping. In Proceedings of the 30th
international on Design automation conference (1993), ACM Press, pp. 54–60.

[13] C OURCOUBETIS , C., AND YANNAKAKIS , M. Verifying temporal properties of finite-state proba-
bilistic programs. In Proceedings of the IEEE Conference on Decision and Control (Piscataway, NJ,
1988), IEEE Press, pp. 338–345.

[14] C OURCOUBETIS , C., AND YANNAKAKIS , M. The complexity of probabilistic verification. J. ACM
42, 4 (1995), 857–907.
196 REFERENCES

[15] DAWS , C., K WIATKOWSKA , M., AND N ORMAN , G. Automatic verification of the IEEE 1394 root
contention protocol with KRONOS and PRISM. In Proc. 7th International Workshop on Formal
Methods for Industrial Critical Systems (FMICS’02) (2002), R. Cleaveland and H. Garavel, Eds.,
vol. 66.2 of Electronic Notes in Theoretical Computer Science, Elsevier.

[16] DE A LFARO , L. Formal verification of performance and reliability of real-time systems. Tech. Rep.
STAN-CS-TR-96-1571, Stanford University, 1996.

[17] D ELLA P ENNA , G., I NTRIGILA , B., M ELATTI , I., M INICHINO , M., C IANCAMERLA , E., PARISSE ,
A., T RONCI , E., AND V ENTURINI Z ILLI , M. Automatic verification of a turbogas control system
with the murϕ verifier. In Hybrid Systems: Computation and Control, 6th International Workshop,
HSCC 2003 Prague, Czech Republic, April 3-5, 2003, Proceedings (2003), O. Maler and A. Pnueli,
Eds., vol. 2623 of Lecture Notes in Computer Science, Springer, pp. 141–155.

[18] D ELLA P ENNA , G., I NTRIGILA , B., M ELATTI , I., T RONCI , E., AND V ENTURINI Z ILLI , M. Finite
horizon analysis of markov chains with the murϕ verifier. In Correct Hardware Design and Verifica-
tion Methods, 12th IFIP WG 10.5 Advanced Research Working Conference, CHARME 2003, L’Aquila,
Italy, October 21-24, 2003, Proceedings (2003), D. Geist and E. Tronci, Eds., vol. 2860 of Lecture
Notes in Computer Science, Springer, pp. 394–409.

[19] D ELLA P ENNA , G., I NTRIGILA , B., M ELATTI , I., T RONCI , E., AND V ENTURINI Z ILLI , M. Fi-
nite horizon analysis of stochastic systems with the murϕ verifier. In Theoretical Computer Sci-
ence, 8th Italian Conference, ICTCS 2003, Bertinoro, Italy, October 13-15, 2003, Proceedings (2003),
C. Blundo and C. Laneve, Eds., vol. 2841 of Lecture Notes in Computer Science, Springer, pp. 58–71.

[20] D ELLA P ENNA , G., I NTRIGILA , B., M ELATTI , I., T RONCI , E., AND V ENTURINI Z ILLI , M.
Bounded probabilistic model checking with the murϕ verifier. In Formal Methods in Computer-Aided
Design, 5th International Confrence, FMCAD 2004, Austin, Texas, USA, November 15-17, 2004, Pro-
ceedings (2004), A. J. Hu and A. K. Martin, Eds., vol. 3312 of Lecture Notes in Computer Science,
Springer, pp. 214–229.

[21] D ELLA P ENNA , G., I NTRIGILA , B., M ELATTI , I., T RONCI , E., AND V ENTURINI Z ILLI , M. Ex-
ploiting transition locality in automatic verification of finite state concurrent systems. STTT 6, 4
(2004), 320–341.

[22] D ILL , D. L., D REXLER , A. J., H U , A. J., AND YANG , C. H. Protocol verification as a hardware
design aid. In Proceedings of the 1991 IEEE International Conference on Computer Design on VLSI
in Computer & Processors (1992), IEEE Computer Society, pp. 522–525.

[23] E ` M C 2 Web Page: http://www7.informatik.uni-erlangen.de/etmcc, 2005.

[24] FHP-Murϕ Web Page: http://www.di.univaq.it/melatti/FHPMurphi/, 2004.

[25] H ANSSON , H. Time and Probability in Formal Design of Distributed Systems. Elsevier, 1994.

[26] H ANSSON , H., AND J ONSSON , B. A logic for reasoning about time and probability. Formal Aspects
of Computing 6, 5 (1994), 512–535.

[27] H ART, S., AND S HARIR , M. Probabilistic temporal logics for finite and bounded models. In Proceed-
ings of the sixteenth annual ACM symposium on Theory of computing (1984), ACM Press, pp. 1–13.

[28] H ARTONAS -G ARMHAUSEN , V., C AMPOS , S. V. A., AND C LARKE , E. M. Probverus: Probabilistic
symbolic model checking. In Formal Methods for Real-Time and Probabilistic Systems, 5th Interna-
tional AMAST Workshop, ARTS’99, Bamberg, Germany, May 26-28, 1999. Proceedings (1999), J.-P.
Katoen, Ed., vol. 1601 of Lecture Notes in Computer Science, Springer, pp. 96–110.

[29] H AVERKORT, B., H ERMANNS , H., AND K ATOEN , J.-P. On the use of model checking techniques for
dependability evaluation. In Proc. 19th IEEE Symposium on Reliable Distributed Systems (SRDS’00)
(Erlangen, Germany, October 2000), pp. 228–237.
REFERENCES 197

[30] H ÉRAULT, T., L ASSAIGNE , R., M AGNIETTE , F., AND P EYRONNET, S. Approximate probabilis-
tic model checking. In Verification, Model Checking, and Abstract Interpretation, 5th International
Conference, VMCAI 2004, Venice, January 11-13, 2004, Proceedings (2004), B. Steffen and G. Levi,
Eds., vol. 2937 of Lecture Notes in Computer Science, Springer, pp. 73–84.

[31] H ERMANNS , H., K ATOEN , J.-P., M EYER -K AYSER , J., AND S IEGLE , M. A tool for model-checking
markov chains. Software Tools for Technology Transfer 4, 2 (Feb 2003), 153–172.

[32] H ERMANNS , H., M EYER -K AYSER , J., AND S IEGLE , M. Multi terminal binary decision diagrams
to represent and analyse continuous time Markov chains. In Proc. 3rd International Workshop on
Numerical Solution of Markov Chains (NSMC’99) (1999), B. Plateau, W. Stewart, and M. Silva, Eds.,
Prensas Universitarias de Zaragoza, pp. 188–207.

[33] H OLZMANN , G. J. Design and Validation of Computer Protocols. Prentice Hall, New Jersey, 1991.

[34] H OLZMANN , G. J. The spin model checker. IEEE Trans. on Software Engineering 23, 5 (May 1997),
279–295.

[35] H U , A. J., YORK , G., AND D ILL , D. L. New techniques for efficient verification with implicitly
conjoined bdds. In DAC ’94: Proceedings of the 31st annual conference on Design automation (New
York, NY, USA, 1994), ACM Press, pp. 276–282.

[36] I BE , O., AND T RIVEDI , K. Stochastic Petri net models of polling systems. IEEE Journal on Selected
Areas in Communications 8, 9 (1990), 1649–1657.

[37] K ATOEN , J.-P., K WIATKOWSKA , M. Z., N ORMAN , G., AND PARKER , D. Faster and symbolic
ctmc model checking. In Process Algebra and Probabilistic Methods, Performance Modeling and
Verification: Joint International Workshop, PAPM-PROBMIV 2001, Aachen, Germany, September
12-14, 2001, Proceedings (2001), L. de Alfaro and S. Gilmore, Eds., vol. 2165 of Lecture Notes in
Computer Science, Springer, pp. 23–38.

[38] K RZYSZTOF, R. A., AND O LDEROG , E.-R. Verification of sequential and concurrent programs.
Springer-Verlag New York, Inc., 1991.

[39] K WIATKOWSKA , M., N ORMAN , G., AND PARKER , D. PRISM: Probabilistic symbolic model
checker. In Computer Performance Evaluation, Modelling Techniques and Tools 12th International
Conference, TOOLS 2002, London, UK, April 14-17, 2002, Proceedings (2002), T. Field, P. G. Har-
rison, J. T. Bradley, and U. Harder, Eds., vol. 2324 of Lecture Notes in Computer Science, Springer,
pp. 200–204.

[40] K WIATKOWSKA , M., N ORMAN , G., AND PARKER , D. Probabilistic symbolic model checking with
PRISM: A hybrid approach. In Tools and Algorithms for the Construction and Analysis of Systems,
8th International Conference, TACAS 2002, Held as Part of the Joint European Conference on Theory
and Practice of Software, ETAPS 2002, Grenoble, France, April 8-12, 2002, Proceedings (2002), J.-P.
Katoen and P. Stevens, Eds., vol. 2280 of Lecture Notes in Computer Science, Springer, pp. 52–66.

[41] K WIATKOWSKA , M. Z., N ORMAN , G., AND S EGALA , R. Automated verification of a randomized
distributed consensus protocol using cadence smv and prism. In Computer Aided Verification, 13th
International Conference, CAV 2001, Paris, France, July 18-22, 2001, Proceedings (2001), vol. 2102
of Lecture Notes in Computer Science, Springer, pp. 194–206.

[42] L AROUSSINIE , F., AND S PROSTON , J. Model checking durational probabilistic systems. In Foun-
dations of Software Science and Computational Structures, 8th International Conference, FOSSACS
2005, Held as Part of the Joint European Conferences on Theory and Practice of Software, ETAPS
2005, Edinburgh, UK, April 4-8, 2005, Proceedings (2005), V. Sassone, Ed., vol. 3441 of Lecture
Notes in Computer Science, Springer, pp. 140–154.
198 REFERENCES

[43] L ARSEN , K. G., P ETTERSSON , P., AND Y I , W. U PPAAL: Status and developments. In Com-
puter Aided Verification, 9th International Conference, CAV ’97, Haifa, Israel, June 22-25, 1997,
Proceedings (1997), O. Grumberg, Ed., vol. 1254 of Lecture Notes in Computer Science, Springer,
pp. 456–459.
[44] L ARSEN , K. G., AND S KOU , A. Bisimulation through probabilistic testing. Inf. Comput. 94, 1
(1991), 1–28.
[45] L EHMANN , D., AND R ABIN , M. On the advantages of free choice: A symmetric fully distributed
solution to the dining philosophers problem (extended abstract). In Proc. 8th Symposium on Principles
of Programming Languages (1981), pp. 133–138.
[46] LYNCH , N., S AIAS , I., AND S EGALA , R. Proving time bounds for randomized distributed algo-
rithms. In Proceedings of the thirteenth annual ACM symposium on Principles of distributed comput-
ing (1994), ACM Press, pp. 314–323.
[47] Murphi Web Page: http://sprout.stanford.edu/dill/murphi.html, 2004.
[48] NuSMV Web Page: http://nusmv.irst.itc.it/, 2004.
[49] PAPOULIS , A. Probability, Random Variables and Stochastic Processes. McGraw-Hill Inc., 1965.
[50] Animated presentation for the examples of Section 4.1.2: http://www.di.univaq.it/melatti/ExamplesThesis,
2005.
[51] P NUELI , A., AND Z UCK , L. Verification of multiprocess probabilistic protocols. Distrib. Comput. 1,
1 (1986), 53–72.
[52] PRISM Web Page: http://www.cs.bham.ac.uk/∼dxp/prism/, 2004.
[53] S EGALA , R., AND LYNCH , N. Probabilistic simulations for probabilistic processes. In CONCUR ’94,
Concurrency Theory, 5th International Conference, Uppsala, Sweden, August 22-25, 1994, Proceed-
ings (1994), B. Jonsson and J. Parrow, Eds., vol. 836 of Lecture Notes in Computer Science, Springer,
pp. 481–496.
[54] S HMATIKOV, V. Probabilistic analysis of anonymity. In Proceedings of the 15th IEEE Computer
Security Foundations Workshop (CSFW’02) (2002), IEEE Computer Society, p. 119.
[55] Eric W. Weisstein. ”Sigma-Algebra”. From MathWorld–A Wolfram Web Resource.
http://mathworld.wolfram.com/sigma-algebra.html, 2005.
[56] SMV Web Page: http://www-2.cs.cmu.edu/∼modelcheck/smv.html, 2004.
[57] SPIN Web Page: http://spinroot.com, 2004.
[58] S TERN , U., AND D ILL , D. L. A new scheme for memory-efficient probabilistic verification. In
IFIP TC6/WG6.1 Joint International Conference on: Formal Description Techniques for Distributed
Systems and Communication Protocols, and Protocol Specification, Testing, and Verification (1996),
R. Gotzhein and J. Bredereke, Eds., vol. 69 of IFIP Conference Proceedings, Kluwer, pp. 333–348.
[59] S TEWART, W. J. Introduction to the Numerical Solution of Markov Chains. Princeton University
Press, 1994.
[60] TwoTowers Web Page: http://www.sti.uniurb.it/bernardo/twotowers/, 2004.
[61] UPPAAL Web Page: http://www.docs.uu.se/docs/rtmv/uppaal/, 2004.
[62] VARDI , M. Automatic verification of probabilistic concurrent finite-state programs. In 26th An-
nual Symposium on Foundations of Computer Science (Portland, Oregon, Oct 1985), IEEE CS Press,
pp. 327–338.

Das könnte Ihnen auch gefallen