ABSTRACT
In this work, we present a case study on the development of
a formally verified Android application for checking medication interactions and contraindications. Combining formal
methods and Model-View-Controller development methodologies, we created an Event-B model for the application,
verified that no patient could be prescribed a medication
that had an interaction or contraindication for them, generated code for the model and part of the user interface with
the EventB2SQL tool, and then implemented the controller
and the rest of the view by hand. We describe our experience in employing this methodology, enhancements to the
EventB2SQL tool, and some notes on the performance and
usability of the resulting application.
Keywords
Event-B, code generation, mobile application development,
database applications, EventB2SQL
1.
INTRODUCTION
http://developer.android.com/
Permission to make digital or hard copies of all or part of this work for
personal or classroom use is granted without fee provided that copies are
not made or distributed for profit or commercial advantage and that copies
bear this notice and the full citation on the first page. To copy otherwise, to
republish, to post on servers or to redistribute to lists, requires prior specific
permission and/or a fee.
Copyright 200X ACM X-XXXXX-XX-X/XX/XX ...$10.00.
2.
2.1
BACKGROUND
Event-B
Event-B [1] is both a notation and a methodology for developing formally verified systems. Event-B models are composed of contexts and machines. Contexts include carrier
2.2
EventB2SQL
erated code to work with instances of any Java class that implements the java.io.Serializable interface (needed for
storing instances in the database). As instances of carrier
sets are added to the database, each is assigned a unique
integer identifier that is used to refer to that instance in the
generated code.
Each event (including the initialisation event) is translated
to a method with the same name and return type boolean. If
the translation of the event guard is not satisfied, the method
returns false and the translation of the event actions is
not executed. Otherwise, the method returns true and the
code generated from the actions is executed to update the
state of the database. EventB2SQL also generates methods
for observing the state of the model, including methods for
observing the value of each machine variable, for returning
the image of a domain element for relations and functions,
and for retrieving objects from carrier sets.
In comparison with other Event-B code generation tools,
EventB2SQL can translate relatively abstract Event-B machines. EventB2SQL can translate machines that use:
variables of arbitrary (finite) function and relation types,
including those with complex domain and range element types such as tuples and powersets
simultaneous assignment
quantified predicates and set comprehensions where
the bound variable ranges over a known and finite domain
Hence, software development efforts that employ EventB2SQL
often have very short refinement chains, thus avoiding the
time and effort needed to produce refinement machines and
discharge refinement proof obligations.
EventB2SQL has been used in the re-development of an
Enterprise Resource Planning system [4]. The EventB2SQL
website includes installation and usage instructions, as well
as examples of how to use the code generated by EventB2SQL
with MySQL, SQLite (JDBC) and SQLite (Android).
3.
The MedicationChecker machine in Figure 1 sees a context MedicationContext (not shown) that introduces three
carrier sets: DRUG, PERSON and CONDITION, representing the sets of all possible medications, patients and medical
conditions. Variables patients, drugs and conditions represent the sets of patients, medications and conditions that
are stored in the system. Variable currentMeds is a relation
from patients to the medications they are currently taking,
hasCondition is a relation from patients to the conditions
they currently have, interacts is a relation holding pairs of
interacting drugs, and contraIndication a relation from conditions to contraindicated medications. Invariant inv6 on
lines 14 and 15 requires the interacts relation to be symmetric, ensuring that an interaction will be detected regardless
of which medication is checked. The primary safety conditions are invariants inv5 on lines 9 - 13 and inv10 on lines
19 - 21. Invariant inv5 ensures that no patient is taking
two medications that interact with each other, and inv10
ensures that no patient is taking a medication that is contraindicated for a condition that they have. The relatively
complex structure of these two invariants stems largely from
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1
2
3
4
5
6
7
8
9
10
11
12
event addMedication
any patient drug
where
grd1: patient patients
grd2: drug drugs \ currentMeds[{patient}]
grd3: drug1 drug1 currentMeds[{patient}]
(drug 7 drug1 6 interacts
drug1 7 drug 6 interacts)
grd4: drug 6 contraIndication[hasCondition[{patient}]]
then
act1: currentMeds:= currentMeds {patient 7 drug}
end
Figure 2: The addMedication event of the MedicationChecker machine.
the need to express these conditions for each individual patient. If the MedicationChecker machine needed to be refined before implementation, these invariants would create
formidable refinement proof obligations.
The addMedication event in Figure 2 is used to prescribe
a new medication for a patient. Guard grd3 ensures that
the medication does not interact with a medication that the
patient is already taking. The redundant check for an interaction in both directions (lines 7 and 8) allows the Rodin
provers to verify that this event maintains invariant inv5
from Figure 1 without further human interaction. Guard
grd4 checks that the new medication is not contraindicated
for any condition that the patient has. The event for diagnosing a patient with a new medical condition (not shown)
is similar, but with simpler guards as there is no need to
check medication interactions.
In addition to checks when prescribing a medication or diagnosing a condition, the MedicationChecker machine must
also ensure that adding a new interaction or contraindica-
1
2
3
4
5
6
7
8
9
10
11
12
event addInteraction
any drug1 drug2
where
grd1: drug1 drugs
grd2: drug2 drugs
grd3: drug1 6= drug2
grd4: patient patient patients
({drug1, drug2} 6 currentMeds[{patient}])
then
act1: interacts := interacts
{drug1 7 drug2, drug2 7 drug1}
end
Figure 3: The addInteraction event of the MedicationChecker machine.
tion to the system does not cause the safety conditions to be
violated. Figure 3 presents the event for adding a new interaction. Guard grd4 on lines 7 and 8 ensures that no patient
is currently taking both of the interacting medications, thus
maintaining invariant inv5 in Figure 1. The event for adding
a new contraindication (not shown) similarly has guards to
ensure that inv10 is maintained.
The MedicationChecker machine also includes events (not
shown) for adding and removing patients, conditions and
medications, for initialising the machine, for removing interactions and contraindications, and for a patient to finish
a course of medication and to recover from a condition.
As we developed the MedicationChecker model, we used
Rodin to discharge the proof obligations notably, those ensuring that all events maintained the invariants expressing
safety conditions (inv5, inv6, inv10). All obligations were
discharged automatically, although we did adjust the statements of some invariants and guards to assist the Rodin
provers. We then used EventB2SQL to generate code directly from the abstract model no refinements were required. This avoided not only the time needed to develop
refinement machines, but more significantly, the time and
effort required to discharge refinement proof obligations. As
noted earlier in this section, invariants inv5 and inv10 would
almost certainly cause difficulty in refinement (and refinement proofs), so avoiding refinement is especially significant
for this model. More generally, EventB2SQLs ability to generate code for relatively abstract models enables short (or
even nonexistent) refinement chains, significantly reducing
the time and effort required for formal systems development.
While we did not refine the model as we developed the
MedicationChecker application, we did make numerous improvements to the EventB2SQL tool itself. These are described in the following section.
4.
ENHANCEMENTS TO EVENTB2SQL
As Android devices ship with SQLite installed, many Android applications store data in an SQLite database. There
is no officially supported version of JDBC for Android, so applications typically use an Android-specific API for database
communication. Accordingly, we modified the translation
performed by EventB2SQL for Android applications in the
following ways (all Android classes mentioned are in the
android.database package):
the generated class extends sqlite.SQLiteOpenHelper
so that it can inherit code for database creation
the generated class uses sqlite.SQLiteStatements rather 1 public void pickDrug ( View view ) {
than JDBC PreparedStatements to precompile database 2
MedicationChecker . DRUGPicker curPick
statements for working with carrier sets
3
= checker . new DRUGPicker () ;
4
if ( patient < 0) {
the generated class uses Cursors rather than JDBC
5
curPick . setElements (
ResultSets to iterate over query results. This required
6
checker . getdrugs () ) ;
changing to post-test loops (rather than pre-test) and
7
}
else
{
0-based indexing (rather than 1-based).
8
curPick . setElements ( checker .
These changes were carefully integrated so as not to disturb
9
getcurrentMedsImage ( patient ) ) ;
existing functionality so that users can now use EventB2SQL 10
}
to generate three different types of database applications: 11
curPick . setTitle ( " Choose medication : " ) ;
JDBC for MySQL and SQLite, and SQLite for Android.
12
curPick . show ( this . getFragmentManager () ,
Additionally, we have extended EventB2SQL to generate 13
" PickDrug " ) ;
part of the user interface for Android applications. As noted 14 }
in Section 2.2, the generated code uses integer identifiers to
refer to elements of carrier sets. For example, the method
Figure 4: Code for creating a dialog for choosing a
generated for the addMedication event in Figure 2 takes two
medication.
parameters of type int one for the patient and one for
the medication. This requires application code that calls
this method to keep track of identifiers for elements of carreturns the image of the specified patient in the currentMeds
rier sets, or to use methods in the generated code to find
relation. Note that all methods called in Figure 4 (except
the identifier for particular elements. In an Android applifor show and getFragmentManager, which are Android API
cation, the patient and medication needed for calling the
methods) are automatically generated by EventB2SQL.
addMedication method should be chosen from a list. This
The callback method (not shown) stores the identifier of
means that application code would need to:
the chosen medication in a field for later use, and displays
display a list of all medications
allow the user to select a medication
find the identifier associated with that medication
use that identifier when calling addMedication
and similarly for patients, and for conditions for calling
methods that take a condition as a parameter. While this
could be done using only methods that older versions of
EventB2SQL already generated, it is certainly not convenient.
To alleviate this issue, EventB2SQL now generates the
following for each carrier set:
a nested interface that declares a single callback method.
The parameters of this method are an int identifier for
an element of a carrier set, and a String representing
that element
a nested subclass of android.app.DialogFragment that
creates a dialog that displays all elements of the carrier
set. When the user selects an element, the callback
method is invoked and passed the element identifier
and string representation of that element.
Additionally methods of the subclass allow client code to set
the title of the dialog, and to specify a particular subset of
the carrier set to display, rather than the entire set.
To use this feature, client code need only create the dialog and implement the callback method. Figure 4 gives
the code used to create the dialog for choosing a medication from a patients set of prescriptions. Field checker is
a reference to a MedicationChecker object used to interact
with the model. If the patient has not yet been selected, the
dialog displays all medications (line 6), using the getdrugs
method to get the set of all medications. Otherwise, only the
medications currently prescribed for the selected patient are
shown (lines 8 and 9). The getcurrentMedsImage method
5.
To build a representative database for evaluating the performance and usability of the MedicationChecker application, we first retrieved a list of popular medications from
the website drugs.com. Eliminating medications that did
not have FDA approval and different brand names for the
same generic medication yielded a list of 351 medications.
We then retrieved all major medication interactions (generic
only) and contraindicated conditions for each of these medications. This process resulted in a database of 1424 medications, 218 conditions, 16519 medication interactions and
1437 contraindications. Note that this database is NOT
complete in that it has contraindications for only the original 351 medications, and interactions only in cases where
at least one of the interacting medications is in the original
Operation
add new patient
add new condition
add new medication
delete patient
delete condition
delete medication
add condition to patient
add medication to patient
add new interaction
add new contraindication
finish medication
recover from condition
remove interaction
remove contraindication
build patient selection dialog
build condition selection dialog
build medication selection dialog
time (ms)
36
34
37
53
8
40
29
25
2470
1943
21
32
41
27
125
27
164
6.
RELATED WORK
http://babelfish.arc.nasa.gov/trac/jpf
http://www.juliasoft.com
5
http://cic.javerianacali.edu.co/~ysperchy/
formal-game
4
7.
8.
REFERENCES