Home / Resources / Definition of PSL operators as syntactic sugar for LTL

Introduction

Coq is an interactive theorem prover. It allows for expressing mathematical assertions, mechanically checks proofs of these assertions, helps find formal proofs, and extracts a certified program from the constructive proof of its formal specification. Coq is not an automated theorem prover but includes automatic theorem proving tactics (procedures) and various decision procedures.

LTL_Coq is a library developed by Cenobio Moisés Vázquez Reyes and available here . It contains a simple formalization of many LTL concepts and theorems. This includes definitions of formulas, paths and LTL semantics. LTL (Linear Temporal Logic) is a modal temporal logic with modalities referring to time.

NuSMV is an open-source model checker which implements symbolic model-checking, using a fixed point algorithm with Binary Decision Diagrams (BDD), where a set of states is represented by a BDD instead of an exhaustive list of individual states. Models are described in SMV language and NuSMV supports the analysis of specifications expressed as invariants or in temporal logics, including LTL and PSL.

pyNuSMV is a Python binding for NuSMV. It is intended to provide a Python interface to NuSMV, allowing to use NuSMV as a library.

Coq source code

PSL operators are mostly syntactic sugar for LTL, which means that we need a definition of LTL semantics first. We rely on the LTL_Coq library. Alos, it means that we can define these operators in Coq with a recursive definition. This is achieved with the Fixpoint keyword.

Require Import Nat.
Require Import LTL.

Infix "-->" := impP (at level 20).

Fixpoint next_event_a_i (b: PathF) (i: nat) (f: PathF) :=
  match i with
  | 0   => bot
  | 1   => ((negP b) U (b ∧ f)) ∨ G(negP b)
  | S n => ((negP b) U (b ∧ (X (next_event_a_i b n f)))) ∨ G(negP b)
  end.

Fixpoint next_event_a (b: PathF) (i j: nat) (f: PathF) :=
  match j with
  | 0   => (next_event_a_i b i f)
  | S n =>
    if ( j <? i ) then
      bot
    else
      if ( j =? i ) then
        (next_event_a_i b i f)
      else
        (next_event_a b i n f) ∧ (next_event_a_i b (n+1) f)
  end.

Definition next_event (b f : PathF) := (next_event_a b 1 1 f).

To observe the behaviour of these operators, we can compute some examples.

Section examples.
  Variable (a b: At).
  Compute (next_event   (Var a)     (Var b)).
  Compute (next_event_a (Var a) 1 1 (Var b)).
  Compute (next_event_a (Var a) 2 2 (Var b)).
  Compute (next_event_a (Var a) 3 3 (Var b)).

  Compute (next_event_a (Var a) 1 2 (Var b)).
  Compute (next_event_a (Var a) 2 3 (Var b)).
  Compute (next_event_a (Var a) 1 3 (Var b)).
End examples.

Verification with NuSMV

Model checker NuSMV provides a function to convert PSL to LTL. We can rely on pyNuSMV to convert the same examples and verify that we obtain the same semantic.

First, we need to write the same examples with SMV syntax, for instance in a file called spec.smv:

MODULE main

PSLSPEC next_event("a")("b");

PSLSPEC next_event_a("a")[1:1]("b");
PSLSPEC next_event_a("a")[2:2]("b");
PSLSPEC next_event_a("a")[3:3]("b");

PSLSPEC next_event_a("a")[1:2]("b");
PSLSPEC next_event_a("a")[2:3]("b");
PSLSPEC next_event_a("a")[1:3]("b");

Then, we write a Python script and import pynusmv. Let's imagine that we have a function recursivelyGetAllNodes() that returns all nodes with the same type from the AST.

import pynusmv
import pynusmv.nusmv.parser.psl.psl

pynusmv.init.init_nusmv()
pynusmv.nusmv.parser.parser.Parser_ReadSMVFromFile("spec.smv")

theTree = pynusmv.nusmv.parser.parser.cvar.parsed_tree
theList = list()
recursivelyGetAllNodes(theTree, pynusmv.nusmv.parser.parser.PSLSPEC, theList)
theList.reverse()

for theSpecNode in theList:
  # separate the name from the property:
  theNameNode = pynusmv.nusmv.node.node.cdr(theSpecNode)
  thePslNode  = pynusmv.nusmv.node.node.car(theSpecNode)
  # convert PSL property to LTL:
  theLtlNode = pynusmv.nusmv.parser.psl.psl.PslNode_convert_psl_to_core(thePslNode)
  #
  thePslString = pynusmv.nusmv.node.node.sprint_node(thePslNode)
  theLtlString = pynusmv.nusmv.node.node.sprint_node(theLtlNode)
  print(thePslString)
  print(" <-> " + theLtlString)
  print("")

Makefile

Compilation of a Coq program is conducted by coqc. We must compile both the library and our program to observe the computation examples.

Also, we must run our Python script to compare the results.

Here is a Makefile that provides the recipe (make sure to indent with tabulations):

PROOF = proof.v
DEPS  = LTL.v

BASENAME = $(basename ${PROOF})
DEPSNAME = $(basename ${DEPS})

all: ${PROOF} $(addsuffix .vos, ${DEPSNAME}) verify.py spec.smv
    coqc -verbose ${PROOF}
    @echo "The proof has run correctly."
    ./verify.py

%.vos : %.v
    coqc -verbose $^

clean:
    $(RM) $(addsuffix .glob, ${DEPSNAME}) $(addsuffix .vo  , ${DEPSNAME})
    $(RM) $(addsuffix .vok , ${DEPSNAME}) $(addsuffix .vos , ${DEPSNAME})
    $(RM) $(addsuffix .aux , $(addprefix ., ${DEPSNAME}))
    $(RM) $(addsuffix .glob, ${BASENAME}) $(addsuffix .vo  , ${BASENAME})
    $(RM) $(addsuffix .vok , ${BASENAME}) $(addsuffix .vos , ${BASENAME})
    $(RM) $(addsuffix .aux , $(addprefix ., ${BASENAME}))

Associated resources

Here is an archive which contains the discussed example and its Makefile. Library LTL_Coq is not distributed here, please refer to its repository to download it.



no cookie, no javascript, no external resource, KISS!