Difference between pages "Mathematical Extensions" and "Modularisation Plug-in"

From Event-B
(Difference between pages)
Jump to navigationJump to search
imported>Pascal
 
imported>Alexili
 
Line 1: Line 1:
Currently the operators and basic predicates of the Event-B mathematical language supported by Rodin are fixed.
+
Return to [[Rodin Plug-ins]]
We propose to extend Rodin to define basic predicates, new operators or new algebraic types.
 
  
== Requirements ==
 
  
=== User Requirements ===
+
<!-- [[Image:Mlogo_big.png|thumb|Provisional Plug-in Logo]] -->
* Binary operators (prefix form, infix form or suffix form).
 
* Operators on boolean expressions.
 
* Unary operators, such as absolute values.
 
: Note that the pipe, which is already used for set comprehension, cannot be used to enter absolute values (''in fact, as in the new design the pipe used in set comprehension is only a syntaxic sugar, the same symbol may be used for absolute value. To be confirmed with the prototyped parser. It may however be not allowed in a first time until backtracking is implemented, due to use of lookahead making assumptions on how the pipe symbol is used. -Mathieu ).
 
* Basic predicates (e.g., the symmetry of relations <math>sym(R) \defi R=R^{-1}</math>).
 
: Having a way to enter such predicates may be considered as syntactic sugar, because it is already possible to use sets (e.g., <math>R \in sym</math>, where <math>sym \defi \{R \mid R=R ^{-1}\}</math>) or functions (e.g., <math>sym(R) = \True</math>, where <math>sym \defi (\lambda R \qdot R \in A \rel B \mid \bool(R = R^{-1}))</math>).
 
* Quantified expressions (e.g., <math>\sum x \qdot P \mid y</math>, <math>\prod x \qdot P \mid y</math>, <math>~\min(S)</math>, <math>~\max(S)</math>).
 
* Types.
 
** Enumerated types.
 
** Scalar types.
 
  
=== User Input ===
+
{{TOCright}}
The end-user shall provide the following information:
 
* keyboard input
 
* Lexicon and Syntax. <br/>More precisely, it includes the symbols, the form (prefix, infix, postfix), the grammar, associativity (left-associative or right associative), commutativity, priority, the mode (flattened or not), ...
 
* Pretty-print. <br/>Alternatively, the rendering may be determined from the notation parameters passed to the parser.
 
* Typing rules.
 
* Well-definedness.
 
  
=== Development Requirements ===
+
The Modularisation plug-in provides facilities for structuring Event-B developments into logical units of modelling, called modules. The module concept is very close to the notion Event-B development (a refinement tree of Event-B machines). However, unlike a conventional development, a module comes with an ''interface''. An interface defines the conditions on how a module may be incorporated into another development (that is, another module). The plug-in follows an approach where an interface is characterised by a list of ''operations'' specifying the services provided by the module. An integration of a module into a main development is accomplished by referring operations from Event-B machine actions using an intuitive procedure call notation.  
* Scalability.
 
  
== Towards a generic AST ==
+
<!-- [[Image:Mlogo_big.png|thumb|left|Provisional Plug-in Logo]] -->
  
The following AST parts are to become generic, or at least parameterised:
+
Please see the [[Modularisation Plug-in Installation Instructions]] for details on obtaining and installing the plug-in.
* [[Constrained_Dynamic_Lexer | Lexer]]
 
* [[Constrained Dynamic Parser | Parser]]
 
* Nodes ( Formula class hierarchy ): parameters needed for:
 
** Type Solve (type rule needed to synthesize the type)
 
** Type Check (type rule needed to verify constraints on children types)
 
** WD (WD predicate)
 
** PrettyPrint (tag image + notation (prefix, infix, postfix) + needs parentheses)
 
** Visit Formula (getting children + visitor callback mechanism)
 
** Rewrite Formula (associative formulæ have a specific flattening treatment)
 
* Types (Type class hierarchy): parameters needed for:
 
** Building the type expression (type rule needed)
 
** PrettyPrint (set operator image)
 
** getting Base / Source / Target type (type rule needed)
 
* Verification of preconditions (see for example <tt>AssociativeExpression.checkPreconditions</tt>)
 
  
=== Vocabulary ===
 
  
An '''extension''' is to be understood as a single additional operator definition.
 
  
=== Tags ===
 
  
Every extension is associated with an integer tag, just like existing operators. Thus, questions arise about how to allocate new tags and how to deal with existing tags.<br />
 
The solution proposed here consists in keeping existing tags 'as is'. They are already defined and hard coded in the <tt>Formula</tt> class, so this choice is made with backward compatibility in mind.
 
  
Now concerning extension tags, we will first introduce a few hypotheses:
+
==Overview==
* Tags_Hyp1: tags are never persisted across sessions on a given platform
 
* Tags_Hyp2: tags are never referenced for sharing purposes across various platforms
 
In other words, cross-platform/session formula references are always made through their ''String'' representation. These assumptions, which were already made and verified for Rodin before extensions, lead us to restrict further considerations to the scope of a single session on a single platform.
 
  
The following definitions hold at a given instant <math>t</math> and for the whole platform.<br />
+
[[Image:Modules1.png|thumb|left|General depiction of a module/interface/environment structure]]
Let <math>\mathit{EXTENSION}_t</math> be the set of extensions supported by the platform at instant <math>t</math>;<br /> let <math>\mathit{tag}_t</math> denote the affectation of tags to a extensions at instant <math>t</math> (<math>\mathit{tag}_t \in \mathit{EXTENSION}_t \pfun \intg</math>);<br /> let <math>\mathit{COMMON}</math> be the set of existing tags defined by the <tt>Formula</tt> class (<math>\mathit{COMMON} \sub \intg</math>).<br /> The following requirements emerge:
 
* Tags_Req1: <math>\forall t \qdot \mathit{tag}_t \in \mathit{EXTENSION}_t \tinj \intg</math>
 
* Tags_Req2: <math>\forall e, t_1,t_2 \qdot \mathit{tag}_{t_1}(e)=\mathit{tag}_{t_2}(e)</math> where <math>t_1, t_2</math> are two instants during a given session
 
* Tags_Req3: <math>\forall t \qdot \ran(\mathit{tag}_t) \cap \mathit{COMMON} = \empty</math>
 
  
The above-mentioned scope-restricting hypothesis can be reformulated into: <math>\mathit{tag}</math> needs not be stable across sessions nor across platforms.
+
While a specification based on a single module may be used to describe fairly large systems
 +
such approach presents a number of limitations. The only specification structuring mechanism
 +
of module is the notion of event. A sufficiently detailed model would correspond to a module
 +
with a substantial number of events. This leads to the scalability problems as the number of
 +
events and actions contained in them is linearly proportional to the number of proof obligations.
 +
Even more important is the requirement to a modeller to keep track of all the details contained
 +
in a large module. This leads to the issue of readability. A large specification lacking multi-level
 +
structuring is hard to comprehend and thus hard to develop.
 +
These and other problems are addressed by structuring a specification into a set ''modules''.
 +
The modules comprising a model are weaved together so that they work on the same global
 +
problem. In addition to the improved structuring and legibility, the structuring into multiple
 +
modules permits the separation between the model of a system and the model its environment.
 +
As a self-contained piece of specification, a module is reusable across a range of developments. A
 +
hypothetical library of models would facilitate formal developments through the reuse of ready
 +
third-party designs.
 +
To couple two modules one has to provide the means for a module to benefit from the
 +
functionality of the another module. A module ''interface'' describes a collection of externally
 +
accessible ''operations'' ; ''interface variables'' permit the observation of a module state by
 +
other modules.  
  
=== Formula Factory ===
 
  
The requirements about tags give rise to a need for centralising the <math>\mathit{tag}</math> relation in order to enforce tag uniqueness.
 
The Formula Factory appears to be a convenient and logical candidate for playing this role. Each time an extension is used to make a formula, the factory is called and it can check whether its associated tag exists, create it if needed, then return the new extended formula while maintaining tag consistency.
 
  
The factory can also provide API for requests about tags and extensions: getting the tag from an extension and conversely.
+
===Module Composition===
  
We also need additional methods to create extended formulæ. A first problem to address is: which type should these methods return ?
+
Module interface allows module users to invoke module operations and observe module external
We could define as many extended types as the common AST API does, namely <tt>ExtendedUnaryPredicate</tt>, <tt>ExtendedAssociativeExpression</tt>, and so on, but this would lead to a large number of new types to deal with (in visitors, filters, …), together with a constraint about which types extensions would be forced to fit into. It is thus preferable to have as few extended types as possible, but with as much parameterisation as can be. Considering that the two basic types <tt>Expression</tt> and <tt>Predicate</tt> have to be extensible, we come to add two extended types <tt>ExtendedExpression</tt> and <tt>ExtendedPredicate</tt>.
+
variables. Construction of a system of modules requires the ability to integrate the observable
 +
behaviour and operations of a module into the specification of another module. Organising
 +
modules into a fitting architecture is essential for realisation of a large-scale model.  
  
ExtendedExpression makeExtendedExpression( ? )
+
====Subordinate Module====
ExtendedPredicate makeExtendedPredicate( ? )
 
  
Second problem to address: which arguments should these methods take ?
+
[[Image:Modules2.png|thumb|Subordinate module diagram]]
Other factory methods take the tag, a collection of children where applicable, and a source location. In order to discuss what can be passed as argument to make extended formulæ, we have to recall that the <tt>make…</tt> factory methods have various kinds of clients, namely:
 
* parser
 
* POG
 
* provers
 
(other factory clients use the parsing or identifier utility methods: SC modules, indexers, …)
 
  
Thus, the arguments should be convenient for clients, depending on which information they have at hand.
+
A module used by another module is said to be a subordinate module. The parent module
The source location does not obviously seem to require any adaptation and can be taken as argument the same way. Concerning the tag, it depends on whether clients have a tag or an extension at hand. Both are intended to be easily retrieved from the factory. As a preliminary choice, we can go for the tag and adjust this decision when we know more about client convenience.
+
has the exclusive privilege of calling the operations of its subordinates and observing their
 +
external variables by including them into its invariant, guards and action before-after predicates.  
 +
External variables of subordinate modules cannot be assigned directly and thus can only appear
 +
on the right-hand side of before-after predicate. Each module may have at most one parent
 +
module. This requirement is satisfied by using a fresh module instance each time a subordinate
 +
module is required.  
  
As for children, the problem is more about their types. We want to be able to handle as many children as needed, and of all possible types. Looking to existing formula children configurations, we can find:
 
* expressions with predicate children: <math>\mathit{bool}(P)</math>
 
* expressions with expression children: <math>E_1 + E_2</math>
 
* predicates with predicate children: <math>P_1 \limp P_2</math>
 
* predicates with expression children: <math>\mathit{partition}(S, E_1, E_2)</math>
 
* mixed operators: <math>\{x \qdot P(x) \mid E(x)\}</math>, but it is worth noting that the possibility of introducing bound variables in extended formulæ is not established yet.
 
Thus, for the sake of generality, children of both types should be supported for both extended predicates and extended expressions.
 
  
ExtendedExpression makeExtendedExpression(int tag, Expression[] expressions, Predicate[] predicates, SourceLocation location)
+
====Operation Call====
ExtendedPredicate makeExtendedPredicate(int tag, Expression[] expressions, Predicate[] predicates, SourceLocation location)
+
The passive observation of a subordinate module state is sufficient only for the simplest composition
 +
scenarious. The notion of a module operation offers a parent module the ability to
 +
request a service of a subordinate module at the moment when it is needed. From the parent model
 +
viewpoint, the call of a subordinate module operation accomplishes two effects: it returns a
 +
vector of values and updates some of the observed variables. The returned values may be used
 +
in an action expression to compute a new module state.
  
=== Defining Extensions ===
+
An operation call, although potentially involving a chain of requests to the nested modules,
 +
is atomic. An operation call is expressed by including a function, corresponding to the called
 +
service, into a before-after predicate expression calculating a new module state. For example,
 +
to compute the sum of the results produced by two subordinate modules, one could write
  
An extension is meant to contain every information and behaviour required by:
+
<math>r:=m.left()+n.right() </math>
* Keyboard
 
* (Extensible Fonts ?)
 
* Lexer
 
* Parser
 
* AST
 
* Static Checker
 
* Proof Obligation Generator
 
* Provers
 
  
==== Keyboard requirements ====
+
where <math>left()</math> and <math>right()</math> are the operation names  provided by the modules ''m'' and ''n''. Names
 +
''m'' and ''n'' are the qualifying prefixes of the corresponding subordinate modules. The arguments
 +
to an operation call declared with formal parameters are supplied as a list of values computed on
 +
the current combined state of the parent and subordinate modules. For instance, an operation
 +
call ''sub'' computing the difference of two integer values, can be used as follows:
  
'''Kbd_req1''': an extension must provide an association combo/translation for every uncommon symbol involved in the notation.
+
<math>r:=m.sub(a,m.sub(b,q))</math>
  
==== Lexer requirements ====
+
For verification purposes, the before-after predicate of an action containing one or more operation calls
 +
is transformed into an equivalent predicate that does use operation calls, based on the operation pre- and post-conditions.
 +
The order of operation invocation is important since, in a general case, the result of a
 +
previous operation of a module affects the result of the next operation for the same module.
  
'''Lex_req1''': an extension must provide an association lexeme/token for every uncommon symbol involved in the notation.
+
===Refinement===
  
==== Parser requirements ====
+
====Internal Detalisation====
  
According to the [[Constrained_Dynamic_Parser| Parser]] page, the following informations are required by the parser in order to be able to parse a formula containing a given extension.
+
[[Image:Modules3.png|thumb|left|Module implementation detalisation]]
  
* symbol compatibility
+
During internal detalisation, a module is considered in isolation from the rest of a system.
* group compatibility
+
Module events and its state are refined preserving the externally observable state
* symbol precedence
+
properties and operation interfaces. Once the refinement relation is demonstrated for a given
* group precedence
+
module, its abstraction can be replaced with a refined version everywhere in a system. Such
* notation (see below)
+
replacement does not require changes in other modules. An important consequence is the
 +
possibility of independent module detalisation in a system constructed of several modules.
 +
During the internal detalisation, operation pre-condition may be weakened, post-condition
 +
may be strengthened and the external part of the invariant may be strengthened as well. There
 +
are limits to this process, such as a requirement of an operation feasibility (post-condition must
 +
describe a non-empty set of states) and non-vacuous external invariant.
  
==== AST requirements ====
+
====Composition====
  
The following hard-coded concepts need to be reified for supporting extensions in the AST. An extension instance will then provide these reified informations to an extended formula class for it to be able to fulfil its API. It is the expression of the missing information for a <tt>ExtendedExpression</tt> (resp. <tt>ExtendedPredicate</tt>) to behave as a <tt>Expression</tt> (resp. <tt>Predicate</tt>). It can also be viewed as the parametrization of the AST.
+
[[Image:Modules4.png|thumb|Introducing subordinate module in refinement]]
  
===== Notation =====
+
One way to refine a module functionality and its state is by an inclusion of an existing module (that may exists only in the form of an interface at that point).
 +
Addition of a subordinate module extends a module state with the external variables of the subordinate and the changes to the newly added state are accomplished through operation
 +
calls. The module invariant and action parts must be extended to link with the state of the included subordinate module. Although a module is added as a whole and at once, the parent
 +
part could evolve through a number of refinement steps to a make a full use of the interface of the included module. An existing subordinate module may be replaced with a module implementing a compatible interface. A replacement module body is not necessarily a refinement of the original module. The interface-level compatibility ensures that a parent module is not affected in a detrimental way by a module change.
  
The notation defines how the formula will be printed. In this document, we use the following convention:
+
====Decomposition====
* <math>e_i</math> is the i-th child expression of the extended formula
 
* <math>p_i</math> is the i-th child predicate of the extended formula
 
  
''Example'': infix operator "<math>\lozenge</math>"
+
[[Image:Modules5.png|thumb|left|Decomposition with modules]]
<math>e_1 \lozenge e_2 \lozenge \ldots \lozenge e_n</math>
 
  
We define the following notation framework:
+
In a case when composition with an existing module seems a fitting development continuation
 +
but there is no suitable existing module, a new module may constructed by splitting a module
 +
state and functionality into two parts. Such process is called module decomposition and is
 +
realised by constructing and including a new module into a decomposed module in such a
 +
manner that the result is a refinement of the original module.
 +
The main decision to make when decomposing a module is the set of variables that are going
 +
to be moved into a new subordinate module. A linking invariant would map the combined states
 +
of the parent and subordinate modules into the original module state. The actions updating
 +
variables of the subordinate module are refined into operation calls. In this manner, the variable
 +
partitioning decision and refinement conditions on a decomposed module shape the interface of
 +
the subordinate module.
  
[[Image:notation_uml.png]]
+
To prove the correctness of a module decomposition it is enough to demonstrate the refinement
 +
relation between the non-decomposed and decomposed module versions. The parent
 +
module interface of the decomposed version must be compatible with the original module interface.  
  
On the "<math>\lozenge</math>" infix operator example, the iterable notation would return sucessively:
+
==Module Interface==
* a <tt>IFormulaChild</tt> with index 1
 
* the <tt>INotationSymbol</tt> "<math>\lozenge</math>"
 
* a <tt>IFormulaChild</tt> with index 2
 
* the <tt>INotationSymbol</tt> "<math>\lozenge</math>"
 
* &hellip;
 
* a <tt>IFormulaChild</tt> with index <math>n</math>
 
  
For the iteration not to run forever, the limit <math>n</math> needs to be known: this is the role of the <tt>mapsTo()</tt> method, which fixes the number of children, called when this number is known (i.e. for a particular formula instance).
+
An interface is a new type of Rodin component. It similar to machine except it may not define events but rather defines one a more operations. Like a machine, an interface may import contexts, declare variables and invariants.  
  
'''Open question''': how to integrate bound identifier lists in the notation?
+
An operation definition is made of the following parts:
 +
* Label - this defines an operation name so that it can be referred by another module;
 +
* Parameters - a vector of (formal) operation parameters;
 +
* Preconditions - a list of predicates defining the states when an operation may be invoked. It is checked that caller always respects these conditions. Like event guards, preconditions also type and constrain operation parameters;
 +
* Return variables - a vector of identifiers used to provide a compound operation return value;
 +
* Postconditions - a list of predicates defining the effect of an operation on interface variables and operation return variables. Like event actions, these must maintain an interface invariant.
  
We may make a distinction between fixed-size notations (like n-ary operators for a given n) and variable-size notations (like associative infix operators).
+
[[Image:Interface_editor.png|thumb|Interface Editor]]
While fixed-size notations would have no specific expressivity limitations (besides parser conflicts with other notations), only a finite set of pre-defined variable-size notation patterns will be proposed to the user.  
 
  
The following features need to be implemented to support the notation framework:
+
An interface has no initialisation event. It is an obligation of an importing module to provide a suitable initial state.
* special meta variables that represent children (<math>e_i</math>, <math>p_i</math>)
 
* a notation parser that extracts a fixed-size notation pattern from a user input string
 
  
===== Well-Definedness =====
+
The following is an example of a very simple interface describing a stack module. It has two variables - the stack top pointer and stack itself, and two operations: push and pop.
  
WD predicates also are user-provided data.
 
  
''Example'':
+
INTERFACE stack
  <math>\mathit{D}(e_1) \land (\forall i \cdot i \in 1\mathit{..}(n-1) \Rightarrow (\mathit{D}(e_i) \Rightarrow \mathit{D}(e_{i+1})))</math>
+
VARIABLES top, stack
 +
INVARIANTS
 +
inv1  :  top ∈ ℕ
 +
inv2  :   stack ∈ 0‥top−1 → ℕ
 +
  OPERATIONS
 +
push  ≙  ANY value PRE
 +
pre1  :  value ∈ ℕ
 +
  RETURN
 +
size
 +
  POST
 +
post1  :  top' = top + 1
 +
post2  :  stack' = stack ∪ {top ↦ value}
 +
post3  :  size' = top + 1
 +
  END
 +
pop  ≙  PRE
 +
pre1  :  top > 0
 +
  RETURN
 +
value
 +
  POST
 +
post1  :  value' = stack(top − 1)
 +
post2  :  top' = top − 1
 +
post3  :  stack' = {top−1} ⩤ stack
 +
  END
 +
END
  
In order to process WD predicates, we need to add the following features to the AST:
 
* the <math>\mathit{D}</math> operator
 
* expression variables (predicate variables already exist)
 
* special expression variables and predicate variables that denote a particular formula child (we need to refer to <math>e_1</math> and <math>e_i</math> in the above example)
 
* a <tt>parse()</tt> method that accepts these special meta variables and the <math>\mathit{D}</math> operator and returns a <tt>Predicate</tt> (a WD Predicate Pattern)
 
* a <tt>makeWDPredicate(aWDPredicatePattern, aPatternInstantiation)</tt> method that makes an actual WD predicate
 
  
===== Type Check =====
+
The interface editor is based on the platform composite editor and follows the same principles and structure.
  
An extension shall give a type rule, which consists in:
+
==Importing a Module==
* type check predicates (addressed in this very section)
 
* a resulting type expression (only for expressions)
 
  
''Example'':
+
To benefit from the services provided by a module one ''imports'' a module into a machine. The plug-in provides machine syntax extension for importing modules into a machine.
<math>(\forall i \cdot \mathit{type}(e_i) = \pow(\alpha)) \land (\forall i,j \cdot \mathit{type}(e_i)=\mathit{type}(e_j))</math>
 
  
Type checking can be reified provided the following new AST features:
+
USES
* the <math>\mathit{type}</math> operator
+
prefix1 : module1
* type variables (<math>\alpha</math>)
+
prefix2 : module2
* the above-mentioned expression variables and predicate variables
+
...
* a <tt>parse()</tt> method that accepts these special meta variables and the <math>\mathit{type}</math> operator and returns a <tt>Predicate</tt> (a Type Predicate Pattern)
+
* a <tt>makeTypePredicate(aTypePredicatePattern, aPatternInstantiation)</tt> method that makes an actual Type predicate
+
Prefix is a string literal used to emulate a dedicated namespace for each module. It has the effect of changing the names of all the imported elements by attaching the specified prefix string. The second parameters of ''Uses'' is a name of an interface component.
  
===== Type Solve =====
+
To import a module one has to know its interface. Only arriving at the implementation stage one cares to collect all the relevant module implementations and assemble them into a single system. During the modelling stage, the focus is always on a particular module. Thus, several teams may work on different modules simultaneously.
  
This section addresses type synthesizing for extended expressions (the resulting type part of a type rule). It is given as a type expression pattern, so that the actual type can be computed from the children.
+
[[Image:Interface_po.png|thumb|left|Interface proof obligations in the Project Explorer]]
 +
 +
This is what happens when importing a module -
 +
* all the constants, given sets and axioms visible to the interface of an imported module is visible to the importing machine, although, if a module prefix is specified, constant and given set names are changed accordingly and axioms are dynamically rewritten to account for such change;
 +
* interface variables and invariants becomes the variables and invariants of the importing machine. The prefixing rule also applies to variables and imported invariants are rewritten to adjust to variable name changes. Technically, imported variables are new concrete variables;
 +
* for static checking purposes, operations appear as constant values or constant relations. These are prefixed as well also, at this stage, they are nothing more than typed identifiers;
  
''Example'':
+
Having added a module import to a machine, one typically proceeds by linking the state of the imported module with the state of the machine. This is done by adding new invariants relating machine and interface variables, much like adding a gluing invariant during refinement. The constants from an imported module may be used at this stage.
  <math>\pow(\mathit{type}(e_1))</math>
 
  
In addition to the requirements for Type Check, the following features are needed:
+
Imported interface variables may be used in invariants, guards and action expressions. They may not, however, be updated directly in event actions. The only way to change a value of an interface variable is by calling one of the interface operations.
* a <tt>parse()</tt> method that accepts special meta variables and the <math>\mathit{type}</math> operator and returns an <tt>Expression</tt> (a Type Expression Pattern)
 
* a <tt>makeTypeExpression(aTypeExpressionPattern, aPatternInstantiation)</tt> method that makes an actual Type expression
 
  
==== Static Checker requirements ====
+
The only place where an imported operation may appear is an action expression. Since it is a state updating relation it may not be a part of a guard or invariant.
{{TODO}}
 
==== Proof Obligation Generator requirements ====
 
{{TODO}}
 
==== Provers requirements ====
 
{{TODO}}
 
  
=== Extension compatibility issues ===
+
=== Calling an operation ===
{{TODO}}
 
  
== User Input Summarization ==
+
To integrate a service provided by an imported module into a main development, event actions are refined to rely on the newly available functionality of an imported module. Interface operations are added into expression with a syntax resembling a operation call:
  
Identified required data entail the following user input:
+
[[Image:Operation_po.png|thumb|Operation pre- and post-conditions are used to describe the operation call in a proof obligation.]]
  
{{TODO}}
+
x := pop
 +
...
 +
y := push(7)
 +
 +
Several operation call may be combined to form complex action expression:
 +
 +
z := push(pop * pop)
  
== Impact on other tools ==
+
There no limit on the way operation calls may be composed (subject to typing and verification conditions) although proof obligation complexity could make it impractical having many nested calls. The following is an event saving number from ''i'' to 0 in a stack:
  
Impacted plug-ins (use a factory to build formulæ):
+
save ≙ WHEN
* <tt>org.eventb.core</tt>
+
grd1: i > 0
: In particular, the static checker and proof obligation generator are impacted.
+
THEN
* <tt>org.eventb.core.seqprover</tt>
+
act1: pos ≔ push(i)
* <tt>org.eventb.pp</tt>
+
act2: i ≔ i − 1
* <tt>org.eventb.pptrans</tt>
+
END
* <tt>org.eventb.ui</tt>
+
 +
 +
==Implementing a Module==
  
== Identified Problems ==
+
Implementing a module is similar to constructing a refinement of a machine. The formal link between a machine and an interface is declared using the new '''Implements''' clause:
The parser shall enforce verifications to detect the following situations:
 
* Two mathematical extensions are not compatible (the extensions define symbols with the same name but with a different semantics).
 
* A mathematical extension is added to a model and there is a conflict between a symbol and an identifier.
 
* An identifier which conflicts with a symbol of a visible mathematical extension is added to a model.
 
  
Beyond that, the following situations are problematic:
+
MACHINE m
* A formula has been written with a given parser configuration and is read with another parser configuration.
+
IMPLEMENTS iface
: As a consequence, it appears as necessary to remember the parser configuration.
+
  ...
: The static checker will then have a way to invalid the sources of conflicts (e.g., priority of operators, etc).
 
:: ''The static checker will then have a way to invalid the formula upon detecting a conflict (name clash, associativity change, semantic change...) [[User:Mathieu|mathieu]]
 
  
* A proof may free a quantified expression which is in conflict with a mathematical extension.
+
Like in refinement, the variables and constants of interface are visible to an implementing machine. However, unlike module import, this time interface variables play the role of abstract variables.
: SOLUTION #1: Renaming the conflicting identifiers in proofs?
 
  
== Open Questions ==
+
An interface operation is realised by a set of events. It is required to provide a realisation (however abstract) for all the interface operations. A connection between an event and operation is established by marking an event as a part of a specific ''event group''.
  
=== New types ===
 
Which option should we prefer for new types?
 
* OPTION #1: Transparent mode.
 
:In transparent mode, it is always referred to the base type. As a consequence, the type conversion is implicitly supported (weak typing).
 
:For example, it is possible to define the <tt>DISTANCE</tt> and <tt>SPEED</tt> types, which are both derived from the <math>\intg</math> base type, and to multiply a value of the former type with a value of the latter type.
 
  
* OPTION #2: Opaque mode.
+
==Examples==
:In opaque mode, it is never referred to the base type. As a consequence, values of one type cannot be converted to another type (strong typing).
 
:Thus, the above multiplication is not allowed.
 
:This approach has at least two advantages:
 
:* Stronger type checking.
 
:* Better prover performances.
 
:It also has some disadvantages:
 
:* need of ''extractors'' to convert back to base types.
 
:* need of extra circuitry to allow things like <math>x:=d*2</math> where <math>x, d</math> are of type <tt>DISTANCE</tt>
 
  
* OPTION #3: Mixed mode.
+
Two small-scale examples are available:
:In mixed mode, the transparent mode is applied to scalar types and the opaque mode is applied to other types.
+
* [[http://iliasov.org/modplugin/ticketmachine.zip]] - a model of queue based on two ticket machine module instantiations (very basic)
 +
* [[http://iliasov.org/modplugin/doors.zip]] - two doors sluice controller specification that is decomposed into a number of independent developments (few first steps only)
  
=== Scope of the mathematical extensions ===
 
* OPTION #1: Project scope.
 
:The mathematical extensions are implicitly visible to all components of the project that has imported them.
 
* OPTION #2: Component scope.
 
:The mathematical extensions are only visible to the components that have explicitly imported them. However, note that this visibility is propagated through the hierarchy of contexts and machines (<tt>EXTENDS</tt>, <tt>SEES</tt> and <tt>REFINES</tt> clauses).
 
:An issue has been identified. Suppose that <tt>ext1</tt> extension is visible to component <tt>C1</tt> and <tt>ext2</tt> is visible to component <tt>C2</tt>, and there is no compatibility issue between <tt>ext1</tt> and <tt>ext2</tt>. It is not excluded that an identifier declared in <tt>C1</tt> conflict with a symbol in <tt>ext2</tt>. As a consequence, a global verification is required when adding a new mathematical extension.
 
  
== Bibliography ==
+
[[Category:Plugin]]
* J.R. Abrial, M.Butler, M.Schmalz, S.Hallerstede, L.Voisin, [http://deploy-eprints.ecs.soton.ac.uk/80 ''Proposals for Mathematical Extensions for Event-B''], 2009.
 
:This proposal consists in considering three kinds of extension:
 
# Extensions of set-theoretic expressions or predicates: example extensions of this kind consist in adding the transitive closure of relations or various ordered relations.
 
# Extensions of the library of theorems for predicates and operators.
 
# Extensions of the Set Theory itself through the definition of algebraic types such as  lists or ordered trees using new set constructors.
 
 
 
[[Category:Design proposal]]
 

Revision as of 09:47, 22 October 2009

Return to Rodin Plug-ins


The Modularisation plug-in provides facilities for structuring Event-B developments into logical units of modelling, called modules. The module concept is very close to the notion Event-B development (a refinement tree of Event-B machines). However, unlike a conventional development, a module comes with an interface. An interface defines the conditions on how a module may be incorporated into another development (that is, another module). The plug-in follows an approach where an interface is characterised by a list of operations specifying the services provided by the module. An integration of a module into a main development is accomplished by referring operations from Event-B machine actions using an intuitive procedure call notation.


Please see the Modularisation Plug-in Installation Instructions for details on obtaining and installing the plug-in.



Overview

General depiction of a module/interface/environment structure

While a specification based on a single module may be used to describe fairly large systems such approach presents a number of limitations. The only specification structuring mechanism of module is the notion of event. A sufficiently detailed model would correspond to a module with a substantial number of events. This leads to the scalability problems as the number of events and actions contained in them is linearly proportional to the number of proof obligations. Even more important is the requirement to a modeller to keep track of all the details contained in a large module. This leads to the issue of readability. A large specification lacking multi-level structuring is hard to comprehend and thus hard to develop. These and other problems are addressed by structuring a specification into a set modules. The modules comprising a model are weaved together so that they work on the same global problem. In addition to the improved structuring and legibility, the structuring into multiple modules permits the separation between the model of a system and the model its environment. As a self-contained piece of specification, a module is reusable across a range of developments. A hypothetical library of models would facilitate formal developments through the reuse of ready third-party designs. To couple two modules one has to provide the means for a module to benefit from the functionality of the another module. A module interface describes a collection of externally accessible operations ; interface variables permit the observation of a module state by other modules.


Module Composition

Module interface allows module users to invoke module operations and observe module external variables. Construction of a system of modules requires the ability to integrate the observable behaviour and operations of a module into the specification of another module. Organising modules into a fitting architecture is essential for realisation of a large-scale model.

Subordinate Module

Subordinate module diagram

A module used by another module is said to be a subordinate module. The parent module has the exclusive privilege of calling the operations of its subordinates and observing their external variables by including them into its invariant, guards and action before-after predicates. External variables of subordinate modules cannot be assigned directly and thus can only appear on the right-hand side of before-after predicate. Each module may have at most one parent module. This requirement is satisfied by using a fresh module instance each time a subordinate module is required.


Operation Call

The passive observation of a subordinate module state is sufficient only for the simplest composition scenarious. The notion of a module operation offers a parent module the ability to request a service of a subordinate module at the moment when it is needed. From the parent model viewpoint, the call of a subordinate module operation accomplishes two effects: it returns a vector of values and updates some of the observed variables. The returned values may be used in an action expression to compute a new module state.

An operation call, although potentially involving a chain of requests to the nested modules, is atomic. An operation call is expressed by including a function, corresponding to the called service, into a before-after predicate expression calculating a new module state. For example, to compute the sum of the results produced by two subordinate modules, one could write

r:=m.left()+n.right()

where left() and right() are the operation names provided by the modules m and n. Names m and n are the qualifying prefixes of the corresponding subordinate modules. The arguments to an operation call declared with formal parameters are supplied as a list of values computed on the current combined state of the parent and subordinate modules. For instance, an operation call sub computing the difference of two integer values, can be used as follows:

r:=m.sub(a,m.sub(b,q))

For verification purposes, the before-after predicate of an action containing one or more operation calls is transformed into an equivalent predicate that does use operation calls, based on the operation pre- and post-conditions. The order of operation invocation is important since, in a general case, the result of a previous operation of a module affects the result of the next operation for the same module.

Refinement

Internal Detalisation

Module implementation detalisation

During internal detalisation, a module is considered in isolation from the rest of a system. Module events and its state are refined preserving the externally observable state properties and operation interfaces. Once the refinement relation is demonstrated for a given module, its abstraction can be replaced with a refined version everywhere in a system. Such replacement does not require changes in other modules. An important consequence is the possibility of independent module detalisation in a system constructed of several modules. During the internal detalisation, operation pre-condition may be weakened, post-condition may be strengthened and the external part of the invariant may be strengthened as well. There are limits to this process, such as a requirement of an operation feasibility (post-condition must describe a non-empty set of states) and non-vacuous external invariant.

Composition

Introducing subordinate module in refinement

One way to refine a module functionality and its state is by an inclusion of an existing module (that may exists only in the form of an interface at that point). Addition of a subordinate module extends a module state with the external variables of the subordinate and the changes to the newly added state are accomplished through operation calls. The module invariant and action parts must be extended to link with the state of the included subordinate module. Although a module is added as a whole and at once, the parent part could evolve through a number of refinement steps to a make a full use of the interface of the included module. An existing subordinate module may be replaced with a module implementing a compatible interface. A replacement module body is not necessarily a refinement of the original module. The interface-level compatibility ensures that a parent module is not affected in a detrimental way by a module change.

Decomposition

Decomposition with modules

In a case when composition with an existing module seems a fitting development continuation but there is no suitable existing module, a new module may constructed by splitting a module state and functionality into two parts. Such process is called module decomposition and is realised by constructing and including a new module into a decomposed module in such a manner that the result is a refinement of the original module. The main decision to make when decomposing a module is the set of variables that are going to be moved into a new subordinate module. A linking invariant would map the combined states of the parent and subordinate modules into the original module state. The actions updating variables of the subordinate module are refined into operation calls. In this manner, the variable partitioning decision and refinement conditions on a decomposed module shape the interface of the subordinate module.

To prove the correctness of a module decomposition it is enough to demonstrate the refinement relation between the non-decomposed and decomposed module versions. The parent module interface of the decomposed version must be compatible with the original module interface.

Module Interface

An interface is a new type of Rodin component. It similar to machine except it may not define events but rather defines one a more operations. Like a machine, an interface may import contexts, declare variables and invariants.

An operation definition is made of the following parts:

  • Label - this defines an operation name so that it can be referred by another module;
  • Parameters - a vector of (formal) operation parameters;
  • Preconditions - a list of predicates defining the states when an operation may be invoked. It is checked that caller always respects these conditions. Like event guards, preconditions also type and constrain operation parameters;
  • Return variables - a vector of identifiers used to provide a compound operation return value;
  • Postconditions - a list of predicates defining the effect of an operation on interface variables and operation return variables. Like event actions, these must maintain an interface invariant.
Interface Editor

An interface has no initialisation event. It is an obligation of an importing module to provide a suitable initial state.

The following is an example of a very simple interface describing a stack module. It has two variables - the stack top pointer and stack itself, and two operations: push and pop.


INTERFACE stack
VARIABLES top, stack
INVARIANTS
	inv1   :   	top ∈ ℕ
	inv2   :   	stack ∈ 0‥top−1 → ℕ
OPERATIONS
	push   ≙  ANY value PRE
			pre1   :   	value ∈ ℕ
		  RETURN
			size
		  POST
			post1   :   	top' = top + 1
			post2   :   	stack' = stack ∪ {top ↦ value}
			post3   :   	size' = top + 1
		  END
	pop   ≙   PRE
			pre1   :   	top > 0	
		  RETURN
			value
		  POST
			post1   :   	value' = stack(top − 1)
			post2   :   	top' = top − 1
			post3   :   	stack' = {top−1} ⩤ stack
		  END
END


The interface editor is based on the platform composite editor and follows the same principles and structure.

Importing a Module

To benefit from the services provided by a module one imports a module into a machine. The plug-in provides machine syntax extension for importing modules into a machine.

USES 
	prefix1 : 		module1
	prefix2 :		module2
	...

Prefix is a string literal used to emulate a dedicated namespace for each module. It has the effect of changing the names of all the imported elements by attaching the specified prefix string. The second parameters of Uses is a name of an interface component.

To import a module one has to know its interface. Only arriving at the implementation stage one cares to collect all the relevant module implementations and assemble them into a single system. During the modelling stage, the focus is always on a particular module. Thus, several teams may work on different modules simultaneously.

Interface proof obligations in the Project Explorer

This is what happens when importing a module -

  • all the constants, given sets and axioms visible to the interface of an imported module is visible to the importing machine, although, if a module prefix is specified, constant and given set names are changed accordingly and axioms are dynamically rewritten to account for such change;
  • interface variables and invariants becomes the variables and invariants of the importing machine. The prefixing rule also applies to variables and imported invariants are rewritten to adjust to variable name changes. Technically, imported variables are new concrete variables;
  • for static checking purposes, operations appear as constant values or constant relations. These are prefixed as well also, at this stage, they are nothing more than typed identifiers;

Having added a module import to a machine, one typically proceeds by linking the state of the imported module with the state of the machine. This is done by adding new invariants relating machine and interface variables, much like adding a gluing invariant during refinement. The constants from an imported module may be used at this stage.

Imported interface variables may be used in invariants, guards and action expressions. They may not, however, be updated directly in event actions. The only way to change a value of an interface variable is by calling one of the interface operations.

The only place where an imported operation may appear is an action expression. Since it is a state updating relation it may not be a part of a guard or invariant.

Calling an operation

To integrate a service provided by an imported module into a main development, event actions are refined to rely on the newly available functionality of an imported module. Interface operations are added into expression with a syntax resembling a operation call:

Operation pre- and post-conditions are used to describe the operation call in a proof obligation.
x := pop
...
y := push(7)

Several operation call may be combined to form complex action expression:

z := push(pop * pop)

There no limit on the way operation calls may be composed (subject to typing and verification conditions) although proof obligation complexity could make it impractical having many nested calls. The following is an event saving number from i to 0 in a stack:

save ≙ WHEN
		grd1: i > 0
	THEN
		act1: pos ≔ push(i)
		act2: i ≔ i − 1
	END
	
	

Implementing a Module

Implementing a module is similar to constructing a refinement of a machine. The formal link between a machine and an interface is declared using the new Implements clause:

MACHINE m
IMPLEMENTS iface
  ...

Like in refinement, the variables and constants of interface are visible to an implementing machine. However, unlike module import, this time interface variables play the role of abstract variables.

An interface operation is realised by a set of events. It is required to provide a realisation (however abstract) for all the interface operations. A connection between an event and operation is established by marking an event as a part of a specific event group.


Examples

Two small-scale examples are available:

  • [[1]] - a model of queue based on two ticket machine module instantiations (very basic)
  • [[2]] - two doors sluice controller specification that is decomposed into a number of independent developments (few first steps only)