Difference between pages "FAQ" and "Modularisation Plug-in"

From Event-B
(Difference between pages)
Jump to navigationJump to search
imported>Son
 
imported>Alexili
m
 
Line 1: Line 1:
== General ==
+
Return to [[Rodin Plug-ins]]
=== What is event-B? ===
 
'''Event-B''' is a formal method for system-level modelling and analysis. Key features of event-B are the use of set theory as a modelling notation, the use of refinement to represent systems at different abstraction levels and the use of mathematical proof to verify consistency between refinement levels. More details are available in http://www.event-b.org/
 
  
=== What is the difference between event-B and the B method? ===
 
  
Event-B is derived from the [http://en.wikipedia.org/wiki/B-Method B method]. Both notations have the same [http://en.wikipedia.org/wiki/Jean-Raymond_Abrial inventor], and share many common concepts (set-theory, refinement, proof obligations, ...) However, they are used for quite different purpose.  The B method is devoted to the development of ''correct by construction'' software, while the purpose of event-B is to model full systems (including hardware, software and environment of operation).
+
<!-- [[Image:Mlogo_big.png|thumb|Provisional Plug-in Logo]] -->
  
Both notations use a mathematical language which are quite close but do not match exactly (in particular, operator precedences are different).
+
{{TOCright}}
  
=== What is Rodin? ===
+
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.  
The '''Rodin Platform''' is an Eclipse-based IDE for Event-B that provides effective support for refinement and mathematical proof. The platform is open source, contributes to the Eclipse framework and is further extendable with plugins.
 
  
=== Where does the ''Rodin'' name come from? ===
+
<!-- [[Image:Mlogo_big.png|thumb|left|Provisional Plug-in Logo]] -->
  
The Rodin platform was initially developed within the European Commission funded Rodin project (IST-511599 ), where Rodin is an acronym for "Rigorous Open Development Environment for Complex Systems” .  Rodin is also the name of a famous French [http://en.wikipedia.org/wiki/Auguste_Rodin sculptor], one of his most famous work being the [http://en.wikipedia.org/wiki/The_Thinker Thinker].
+
Please see the [[Modularisation Plug-in Installation Instructions]] for details on obtaining and installing the plug-in.
  
== General Tool Usage? ==
 
  
=== How do I install external plug-ins without using Eclipse Update Manager? ===
 
Although it is preferred to install additional plug-ins into the Rodin platform using the Update Manager of Eclipse, this might not always be practical. In this case, a manner to install these plug-ins is to emulate either manually or using ad-hoc scripts the operations normally performed by the Update Manager.
 
  
This manual installation of plug-ins is described in ''[[Installing external plug-ins manually]]''.
 
  
=== The builder takes too long ===
+
==Overview==
Generally, the builder spends most of its time attempting to prove POs.  There are basically two ways to get it out of the way:
 
* the first one is to disable the automated prover in the Preferences panel.
 
* the second one is to mark some PO as reviewed when you don't want the auto-prover to attempt them anymore.
 
Note that if you disable the automated prover, you always can run it later on some files by using the contextual menu in the event-B Explorer.
 
  
To disable the automated prover, open Rodin <tt>Preferences</tt> (menu {{menu|Window > Preferences...}}).  In the tree in the left-hand panel, select {{menu|Event-B > Sequent Prover > Auto-tactic}}. Then, in the right-hand panel ensure that the checkbox labelled <tt>Enable auto-tactic for proving</tt> is disabled.
+
[[Image:Modules1.png|thumb|left|General depiction of a module/interface/environment structure]]
  
To review a proof obligation, just open it in the interactive prover, then click on the ''review'' button (this is a round blue button with a ''R'' in the proof control toolbar). The proof obligation should now labelled with the same icon in the event-B explorer.
+
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.  
  
===What are the ASCII shortcuts for mathematical operators===
 
  
The ASCII shortcuts that can be used for entering mathematical operators are described in the help of the event-B keyboard plug-in.  In the help system, this page has the following path {{menu|Event-B Keyboard User Guide > Getting Started > Special Combos}}.
 
  
This page is also available in the dynamic help system.  The advantage of using dynamic help is that it allows to display the help page side-by-side with the other views and editors.  To start the dynamic help, click {{menu|Help > Dynamic Help}}, then click {{menu|All Topics}} and select the page in the tree.
+
===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.
  
===Rodin (and eclipse) doesn't take into account the MOZILLA_FIVE_HOME environment variable===
 
You have to add a properties by appending the following code to your {{file|eclipse/eclipse.ini}} or {{file|rodin/rodin.ini}} file:
 
-Dorg.eclipse.swt.browser.XULRunnerPath=/usr/lib/xulrunner/xulrunner-xxx
 
  
== Modeling ==
 
=== Witness for {{Ident|Xyz}} missing. Default witness generated ===
 
A parameter as disappeared during a refinement. If this is intentional, you have to add a [[Witnesses (Modelling Language)|witness]] telling how the abstract parameter is refined.
 
  
=== Identifier {{ident|Xyz}} should not occur free in a witness ===
+
====Subordinate Module====
You refer to {{ident|Xyz}} in a witness predicate where {{ident|Xyz}} is a disappearing abstract variable or parameter which is not set as the witness label.
 
  
=== In {{event|INITIALISATION}}, I get  ''Witness {{ident|Xyz}} must be a disappearing abstract variable or parameter'' ===
+
[[Image:Modules2.png|thumb|Subordinate module diagram]]
The witness is for the after value of the abstract variable, hence you should use the primed variable. The witness label should be {{ident|Xyz'}}, and the predicate should refer to {{ident|Xyz'}} too.
 
  
=== I've added a witness for {{ident|Xyz}} but it keeps saying ''"Identifier {{ident|Xyz}} has not been defined"'' ===
+
A module used by another module is said to be a subordinate module. The parent module
As specified in the [[Witnesses (Modelling Language)|modelling language manual]], the witness must be labelled by the name {{Ident|Xyz}} of the concrete variable being concerned.
+
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.  
  
=== What are type expressions in Event-B? ===
 
The type expressions are defined recursively as follows:
 
  
* Built-in types: ℤ, BOOL (Note that ℕ is NOT a type expressions).
 
  
* Any carrier set is a type expression.
+
====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.  
  
* If <math>T</math> is a type expression then <math>ℙ(T)</math> is a type expression.
+
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
  
* If <math>S</math> and <math>T</math> are type expressions then <math>S × T</math> is a type expression.
+
<math>r:=m.left()+n.right() </math>
  
== Proving ==
+
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:
  
=== How can I do a Proof by Induction? ===
+
<math>r:=m.sub(a,m.sub(b,q))</math>
[[Induction proof|This page about proof by induction]] will give you some starting tips.
 
  
=== Labels  of proof tree nodes explained ===
+
For verification purposes, the before-after predicate of an action containing one or more operation calls
* {{ident|ah}} means ''add hypothesis'',
+
is transformed into an equivalent predicate that does use operation calls, based on the operation pre- and post-conditions.
* {{ident|eh}} means rewrite with ''equality from hypothesis'' from left to right,
+
The order of operation invocation is important since, in a general case, the result of a
* {{ident|he}} means rewrite with ''equality from hypothesis'' from right to left,
+
previous operation of a module affects the result of the next operation for the same module.
* {{ident|rv}} tell us that this goal as been manually [[The_Proving_Perspective_(Rodin_User_Manual)#The_Proof_Control_Window|reviewed]],
 
* {{ident|sl/ds}} means ''selection/deselection'',
 
* {{ident|PP}} means ''discharged by the predicate prover''
 
* {{ident|ML}} means ''discharged by the mono lemma prover''
 
  
== How to contribute? ==
 
See the [[How_To_Contribute|How to contribute]] page.
 
  
== Developer FAQ ==
 
=== How can I get the sources of the Rodin platform? ===
 
  
There are two options for getting the sources of the Rodin platform:
 
# The safe option is to download the source bundle which is made available with each platform release.  This ensures that you will get a consistent set of source files, albeit maybe a bit outdated.
 
# The other option is to fetch the sources from Subversion, which allows to get their latest version.  The Rodin developers strive for keeping theses sources usable (i.e. they should always compile and pass unit tests), but breakage can happen.
 
  
=== Installing the source bundle in Eclipse ===
+
===Refinement===
  
To install the bundled sources, you first have to download the bundle from SourceForge.  It takes the form of a ZIP file named <tt>rodin-VERSION-sources.zip</tt>.  Then, in Eclipse, click {{Menu|File > Import...}}  In the ''Import'' popup, select {{Menu|General > Existing Projects into Workspace}} and click {{button|Next}}.  Then tick {{Menu|Select archive file}} and enter the path to the source bundle you have just downloaded.  Ensure that all projects are ticked and click {{button|Finish}}.  Your workspace gets populated with the source projects of the Rodin platform and Eclipse starts building.
+
====Internal Detalisation====
  
=== Installing the sources from Subversion in Eclipse ===
+
[[Image:Modules3.png|thumb|left|Module implementation detalisation]]
  
To install the sources from Subversion, in Eclipse click {{Menu|File > Import...}}, then select {{Menu|SVN > Checkout Projects from SVN}} in the ''Import'' popup and click {{button|Next}} twice. In the ''Url'' textbox, enter <tt>https://rodin-b-sharp.svn.sourceforge.net/svnroot/rodin-b-sharp</tt> and click {{button|Next}}.  In the tree displaying the subversion repository, select path <tt>trunk/RodinCore/org.rodinp.releng</tt> and click {{button|Finish}}.  Your workspace now contains the project <tt>org.rodinp.releng</tt>.
+
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.  
  
To install the other source projects, click again {{Menu|File > Import...}}, but this time select {{Menu|Team > Team Project Set}} and click {{button|Next}}.  When asked for a ''File name'', enter the path to file <tt>org.rodinp.releng/rodin-core.psf</tt> in your workspace and click {{button|Finish}}.  Eclipse then fetches the other source projects of the Rodin platform into your workspace (this can take some time).
 
  
=== Using Rodin-SVN from Eclipse consumes too much memory ===
 
Running the Rodin platform from Eclipse can consume a lot of RAM and become impractical on a small machine.  If you fall in this case, you can [[#How_do_I_generate_a_Rodin_product_from_SVN.3F|generate a product]] and use it as if it was a normal release.
 
  
=== How do I generate a ''Rodin'' product from sources? ===
+
====Composition====
In the project ''org.rodinp.platform'', right-click on ''Rodin.platform'' and select {{menu|export}}. Choose {{menu|Plug-in Development > Eclipse product}} and click on {{button|Next}} type <tt>Rodin</tt> for the ''Root directory'', and choose the ''Destination directory''. Then click on {{button|Finish}}.
 
  
=== How to build ''Rodin'' headless? ===
+
[[Image:Modules4.png|thumb|Introducing subordinate module in refinement]]
When releasing a new version of the Rodin platform, the Rodin team builds the platform headless (i.e. using batch scripts rather than Eclipse GUI).  The process used for that is described in [[Building Rodin Headless]]. Alternatively, the Rodin team has also set up a [[CruiseControl]] server for checking that builds do not break.
 
  
=== How do I collect debug information from the Rodin platform? ===
+
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).
You may see the log in the console by appending <tt>-consoleLog</tt> to the rodin executable: <code>rodin -consoleLog</code>
+
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.
  
You may add specific debug informations by setting specific options: <code>rodin --debug options.file -consoleLog</code> where {{file|options.file}} contains something like:
 
<pre>
 
org.pluginname/debug = true
 
org.pluginname/debug/optionaldebug = true
 
</pre>
 
where'' optionaldebug'' may be found in the {{file|org.pluginname/.options}} file in the [http://rodin-b-sharp.cvs.sourceforge.net/rodin-b-sharp/ rodin source repository].
 
  
=== How do I submit a patch? ===
 
Good practises for patch submission are described [[How to Submit Patches|here]].
 
  
=== How do I track memory leaks? ===
+
====Decomposition====
If you suspect that some memory isn't freed, you may find some useful directions on how to track memory leaks [[Tracking Memory Leaks|here]].
 
  
=== How do I report a bug. ===
+
[[Image:Modules5.png|thumb|left|Decomposition with modules]]
See [[How_To_Contribute#How_do_I_report_a_bug_or_a_feature_request.3F|the ''How to contribute'' page]].
 
  
=== How do I save the models ? ===
+
In a case when composition with an existing module seems a fitting development continuation
After the separation between a file ({{class|IRodinFile}}) and its root (a {{class|IInternalElement}}), that occurred in version 0.9.2, model saving is no more achievable through internal elements. Instead, you have to save the {{class|IRodinFile}}.
+
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.  
  
IInternalElement element = ...
+
To prove the correctness of a module decomposition it is enough to demonstrate the refinement
element.getRodinFile().save(...);
+
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.  
  
  
=== How do I add a new attribute to existing elements (e.g. Event-B event)? ===
 
You need to extend the following extension point <code>org.rodinp.core.attributeTypes</code> to declare the new attribute.
 
  
Note: There is no constraint on which element type this attribute type associated with. In principle, any attribute type can be attached to the any internal element type. 
 
  
The detail steps are described in the following page [[Extending the Rodin Database#Adding a New Attribute|Extending the Rodin Database]].
 
  
=== How do I extend the Event-B structure editor (the Edit Tab of the Event-B editor) for editing an attribute (which I added to the Rodin DB as describe [[#How do I add a new attribute to existing elements (e.g. Event-B event)?| here]])? ===
+
==Module Interface==
  
[[Category:User FAQ]]
+
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.
[[Category:Developer FAQ]]
+
 
 +
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.
 +
 
 +
[[Image:Interface_editor.png|thumb|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.
 +
 
 +
[[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;
 +
 
 +
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:
 +
 
 +
[[Image:Operation_po.png|thumb|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:
 +
* [[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)
 +
 
 +
[[Category:Plugin]]

Revision as of 12:20, 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)