The past decade has witnessed the evolution of workflow specification frameworks from the traditional process-centric approach towards data-awareness. Process-centric formalisms focus on control flow while under-specifying the underlying data and its manipulations by the process tasks, often abstracting them away completely. In contrast, data-aware formalisms treat data as first-class citizens. A notable exponent of this class is IBM’s business artifact model pioneered in , successfully deployed in practice [7, 6, 10, 14, 50] and adopted in industrial standards.
In a nutshell, business artifacts (or simply “artifacts”) model key business-relevant entities, which are updated by a set of services that implement business process tasks, specified declaratively by pre- and-post conditions. A collection of artifacts and services is called an artifact system. IBM has developed several variants of artifacts, of which the most recent is Guard-Stage-Milestone (GSM) [12, 29]. The GSM approach provides rich structuring mechanisms for services, including parallelism, concurrency and hierarchy, and has been incorporated in the OMG standard for Case Management Model and Notation (CMMN) [35, 37].
Artifact systems deployed in industrial settings typically specify complex workflows prone to costly bugs, whence the need for verification of critical properties. Over the past few years, the verification problem for artifact systems has been intensively studied. Rather than relying on general-purpose software verification tools suffering from well-known limitations, the focus of the research community has been to identify practically relevant classes of artifact systems and properties for which fully automatic verification is possible. This is an ambitious goal, since artifacts are infinite-state systems due to the presence of unbounded data. However, verification was shown to be decidable for significant classes of properties and artifact models.
The present paper bridges the gap between the theory and practice of artifact verification by studying the implementation of a full-fledged and efficient artifact verifier. The artifact model we verify is a variant of the Hierarchical Artifact System (HAS) model presented in . In brief, a HAS consists of a database and a hierarchy (rooted tree) of tasks. Each task has associated to it local evolving data consisting of a tuple of artifact variables and an updatable artifact relation. It also has an associated set of services. Each application of a service is guarded by a pre-condition on the database and local data and causes an update of the local data, specified by a post condition (constraining the next artifact tuple) and an insertion or retrieval of a tuple from the artifact relation. In addition, a task may invoke a child task with a tuple of parameters, and receive back a result if the child task completes. A run of the artifact system is obtained by any valid interleaving of concurrently running task services. Properties of HAS are specified using an extension of Linear-Time Temporal logic (LTL).
In a previous study , we made a first attempt at implementing a verifier for a simple version of HAS using Spin , the verification tool widely used in the model checking community. However, as discussed in , Spin cannot handle some of the most useful features of artifacts which support unbounded data, such as sets of tuples (see Section 2 for details). Moreover, its performance is disappointing even after deploying a battery of non-trivial optimizations. This indicates the limited applicability of existing tools for HAS verification and suggests the need for tailored approaches.
In this paper we present VERIFAS, an artifact verifier implementation built from scratch. Our main contributions are the following.
We define HAS*, a novel variant of HAS which strikes a more practically relevant trade-off between expressivity and verification complexity, as demonstrated by its ability to specify a realistic set of business processes. We adapt to HAS* the theory developed in , laying the groundwork for our implementation.
We implement VERIFAS, a fully automatic verifier for HAS*. The implementation makes crucial use of novel optimization techniques, with dramatic impact on performance. The optimizations are non-trivial and include concise symbolic representations, aggressive pruning in the search algorithm, and the use of highly efficient data structures.
We evaluate the performance of VERIFAS using both real-world and synthetic artifact systems and properties from a benchmark we create, bootstrapping from existing sets of business process specifications and properties by extending them with data-aware features. To our knowledge, this is the first benchmark for business processes and properties that includes such data aware features. The experiments highlight the impact of the various optimizations and parameters of both the artifact systems and properties.
We adapt to HAS* a standard complexity measure of control flow used in software engineering, cyclomatic complexity , and show experimentally, using the above benchmark, that cyclomatic complexity of HAS* specifications correlates meaningfully with verification times. Since conventional wisdom in software engineering holds that well-designed, human readable programs have relatively low cyclomatic complexity, this is an indication that verification times are likely to be good for well-designed HAS* specifications.
Taking this and other factors into account, the experimental results show that our verifier performs very well on practically relevant classes of artifact systems. Compared to the Spin-based verifier of , it not only applies to a much broader class of artifacts but also has a decisive performance advantage even on the simple artifacts the Spin-based verifier is able to handle. To the best of our knowledge, this is the first implementation of practical significance of an artifact verifier with full support for unbounded data.
The paper is organized as follows. We start by introducing in Section 2 the HAS* model supported by VERIFAS, and we review LTL-FO, the temporal logic for specifying properties of HAS*. Section 3 describes the implementation of VERIFAS by first reviewing in brief the theory developed of , particularly the symbolic representation technique used in establishing the theoretical results. We show an extension of the symbolic representation, called partial isomorphism type, to allow practical verification by adapting the classic Karp-Miller algorithm . We then introduce three specialized optimizations to gain further performance improvement. We present our experimental results in Section 4. Finally, we discuss related work in Section 5 and conclude in Section 6. An appendix provides further technical details, our full running example, and a table of symbols.
2 The Model
In this section we present the variant of Hierarchical Artifact Systems used in our study. The variant, denoted HAS*, differs from the HAS model used in  in two respects. On one hand, it restricts HAS as follows:
it disallows arithmetic in service pre-and-post conditions
the underlying database schema uses an acyclic set of foreign keys
On the other hand, HAS* extends HAS by removing various restrictions:
tasks may have multiple updatable artifact relations
each subtask of a given task may be called multiple times between task transitions
certain restrictions on how variables are passed as parameters among tasks, or inserted/retrieved from artifact relations, are lifted
Because HAS* imposes some restrictions on HAS but removes others, it is incomparable to HAS. Intuitively, the choice of HAS* over HAS as a target for verification is motivated by the fact that HAS* achieves a more appealing trade-off between expressiveness and verification complexity. The acyclic schema restriction, satisfied by the widely used Star (or Snowflake) schemas [31, 48], is acceptable in return for the removal of various HAS restrictions limiting modeling capability. Indeed, as shown by our real-life examples, HAS* is powerful enough to model a wide variety of business processes. While the current version of VERIFAS does not handle arithmetic, the core verification algorithm can be augmented to include arithmetic along the lines developed for HAS in . Limited use of aggregate functions can also be accommodated. These enhancements are left for future work.
We now present the syntax and semantics of HAS*. The formal definitions below are illustrated with an intuitive example of the HAS* specification of a real-world order fulfillment business process originally written in BPMN . The workflow allows customers to place orders and the supplier company to process the orders. A detailed description of the example can be found in Appendix B.
We begin by defining the underlying database schema.
A database schema is a finite set of relation symbols, where each relation of has an associated sequence of distinct attributes containing the following:
a key attribute ID (present in all relations),
a set of foreign key attributes , and
a set of non-key attributes disjoint from
To each foreign key attribute of is associated a relation of and the inclusion dependency111The inclusion uses set semantics. . It is said that references .
The assumption that the ID of each relation is a single attribute is made for simplicity, and multiple-attribute IDs can be easily handled.
A database schema is acyclic if there are no cycles in the references induced by foreign keys. More precisely, consider the labeled graph FK whose nodes are the relations of the schema and in which there is an edge from to labeled with if has a foreign key attribute referencing . The schema is acyclic if the graph FK is acyclic. All database schemas considered in this paper are acyclic.
The order fulfillment workflow has the following database schema:
In the schema, the IDs are key attributes, , , , , are non-key attributes, and is a foreign key attribute satisfying the dependency . Intuitively, the table contains customer information with a foreign key pointing to the customers’ credit records stored in . The table contains information on the items. Note that the schema is acyclic as there is only one foreign key reference from to .
We assume two infinite, disjoint domains of IDs and data values, denoted by and , and an additional constant where ( serves as a convenient default initialization value). The domain of all non-key attributes is . The domain of each key attribute ID of relation is an infinite subset of , and for . The domain of a foreign key attribute referencing is . Intuitively, in such a database schema, each tuple is an object with a globally unique id. This id does not appear anywhere else in the database except in foreign keys referencing it. An instance of a database schema is a mapping associating to each relation symbol a finite relation (set of tuples) of the same arity of , whose tuples provide, for each attribute, a value from its domain. In addition, satisfies all key and inclusion dependencies associated with the keys and foreign keys of the schema. The active domain , denoted , consists of all elements of .
We next proceed with the definition of tasks and services, described informally in the introduction. Similarly to the database schema, we consider two infinite, disjoint sets of ID variables and of data variables. We associate to each variable its domain . If , then , and if , then . An artifact variable is a variable in . If is a sequence of artifact variables, a valuation of is a mapping associating to each variable in an element in .
A task schema over database schema is a tuple where is a sequence of artifact variables, is a set of relation symbols not in , and and are subsequences of . For each relation , we denote by the set of attributes of . The domain of each variable and each attribute is either or for some relation . In the latter case we say that the type of (or ) is (). An instance of is a tuple where is a valuation of and is an instance of such that is of the type of for each .
We refer to the relations in as the artifact relations of and to and as the input and output variables of . We denote by and .
The order fulfillment workflow has a task called ProcessOrders, which stores the order data and processes the orders by interacting with other tasks. It has the following artifact variables:
ID variables: of type and of type
non-ID variables: and
There are no input or output variables. The task also has an artifact relation with attributes of the same types as the variables. Intuitively, stores the orders to be processed, where each order consists of a customer and an ordered item. The variable indicates the current status of the order and indicates whether the item is currently in stock.
We next define artifact schemas, essentially a hierarchy of task schemas with an underlying database.
An artifact schema is a tuple where is a database schema and is a rooted tree of task schemas over with pairwise disjoint sets of artifact variables and distinct artifact relation symbols.
The rooted tree defines the task hierarchy. Suppose the set of tasks is . For uniformity, we always take task to be the root of . We denote by (or simply when is understood) the partial order on induced by (with the minimum). For a node of , we denote by tree(T) the subtree of rooted at , the set of children of (also called subtasks of ), the set of descendants of (excluding ). Finally, denotes . We denote by the relational schema . An instance of is a mapping associating to each a finite relation of the same type.
The order fulfillment workflow has 5 tasks: : ProcessOrders, :TakeOrder, :CheckCredit, : Restock and :ShipItem, which form the hierarchy represented in Figure 1. Intuitively, the root task ProcessOrders serves as a global coordinator which maintains a pool of all orders and the child tasks TakeOrder, CheckCredit, Restock and ShipItem implement the 4 sequential stages in the fulfillment of an order. At a high level, ProcessOrders repeatedly picks an order from its pool and processes it with a stage by calling the corresponding child task. After the child task returns, the order is either placed back into the pool or processed with the next stage. For each order, the workflow first obtains the customer and item information using the TakeOrder task. The credit record of the customer is checked by the CheckCredit task. If the record is good, then ShipItem can be called to ship the item to the customer. If the requested item is unavailable, then Restock must be called before ShipItem to procure the item.
An instance of an artifact schema is a tuple where is a finite instance of , a finite instance of , a valuation of , and (standing for “stage”) a mapping of to .
The stage of a task has the following intuitive meaning in the context of a run of its parent: says that has been called and has not yet returned its answer, and indicates that is not active. A task can be called any number of times within a given run of its parent, but only one instance of it can be active at any given time.
Figure 2 shows a partial example of an instance of the Order Fulfillment artifact system. The only active task is ProcessOrder.
For a given artifact schema and a sequence of variables, a condition on is a quantifier-free FO formula over whose variables are included in . The special constant can be used in equalities. For each atom of relation , and . If is a condition on , an instance of and a valuation of , we denote by the fact that satisfies with valuation , with standard semantics. For an atom in where , if for some , then is false (because does not occur in database relations). Although conditions are quantifier-free, FO conditions can be easily simulated by adding variables to , so we use them as shorthand whenever convenient.
The FO condition
states that the customer with ID has good credit.
We next define services of tasks. We start with internal services, which update the artifact variables and artifact relation of the task. Intuitively, internal services implement local actions, that do not involve any other task.
Let be a task of an artifact schema . An internal service of is a tuple where:
and , called pre-condition and post-condition, respectively, are conditions over
is the set of propagated variables, where ;
, called the update, is a subset of of size at most 1.
Intuitively, an internal service of can be called only when the current instance satisfies the pre-condition . The update on variables is valid if the next instance satisfies the post-condition and the values of propagate variables stay unchanged.
Any task variable that is not propagated can be changed arbitrarily during a task activation, as long as the post condition holds. This feature allows services to also model actions by external actors who provide input into the workflow by setting the value of non-propagated variables. Such actors may even include humans or other parties whose behavior is not deterministic. For example, a bank manager carrying out a “loan decision” action can be modeled by a service whose result is stored in a non-propagated variable and whose value is restricted by the post-condition to either “Approve” or “Deny”. Note that deterministic actors are modeled by simply using tighter post-conditions.
When , a tuple containing the current value of is inserted into . When , a tuple is chosen and removed from and the next value of is assigned with the value of the tuple. Note that are always propagated, and no other variables are propagated if . The restriction on updates and variable propagation may at first appear mysterious. Its underlying motivation is that allowing simultaneous artifact relation updates and variable propagation turns out to raise difficulties for verification, while the real examples we have encountered do not require this capability.
The ProcessOrders task has 3 internal services: Initialize, StoreOrder and RetrieveOrder. Intuitively, Initialize creates a new order with . When RetrieveOrder is called, an order is chosen non-deterministically and removed from for processing, and is set to be the chosen tuple. When StoreOrder is called, the current order is inserted into . The latter two services are specified as follows.
The sets of propagated variables are empty for both services.
An internal service of a task specifies transitions that modify the variables of and the contents of . Figure 3 shows an example of a transition that results from applying the service StoreOrder of the ProcessOrders task.
As seen above, internal services of a task cause transitions on the data local to the task. Interactions among tasks are specified using two kinds of special services, called the opening-services and closing-services. Specifically, each task is equipped with an opening service and a closing service . Each non-root task can be activated by its parent task via a call to which includes passing parameters to that initialize its input variables . When terminates (if ever), it returns to the parent the contents of its output variables via a call to . Moreover, calls to are guarded by a condition on the parent’s artifact variables, and closing calls to are guarded by a condition on the artifact variables of . The formal definition is provided in Appendix A.
For uniformity of notation, we also equip the root task with a service with pre-condition true and a service whose pre-condition is false (so it never occurs in a run). For a task we denote by the set of its internal services, , and . Intuitively, consists of the services observable locally in runs of task .
As the root task, the opening condition of ProcessOrders is and closing condition is . All variables are initialized to .
The opening condition of TakeOrder is in task ProcessOrders, meaning that the customer and item information have not yet been entered by the customer. The task contains , , and as variables (with no input variable). When this task is called, the customer enters the information of the order ( and ) and the status of the order is set to “OrderPlaced". An external service determines whether the item is in stock or not and sets the value of accordingly. All variables are output variables returned to the parent task. The closing condition is . When it holds, TakeOrder can be closed, and the values of these variables are passed to ProcessOrders (to the variables with the same names222While the formal definition disallows using the same variable names in different tasks, we do so for convenience, since the variable names can be easily disambiguated using the task name.). Figure 4 illustrates a transition caused by the closing service of TakeOrder.
We are finally ready to define HAS*.
A Hierarchical Artifact System* (HAS*) is a triple , where is an artifact schema, is a set of services over including and for each task of , and is a condition over (the global pre-condition of ), where is the root task.
We next define the semantics of HAS*. Intuitively, a run of a HAS* on a database consists of an infinite sequence of transitions among HAS* instances (also referred to as configurations, or snapshots), starting from an initial artifact tuple satisfying pre-condition , and empty artifact relations. The intuition is that at each snapshot, a transition can be made at an active task by applying either an internal service of , the opening service of an inactive subtask , or the closing service of . In addition, we require that an internal service of can only be applied after all active subtasks of have returned their answer. Given two instances , and a service , we denote by if there is a valid transition from to by applying . The full definition of transitions can be found in Appendix A.
We next define runs of artifact systems. We will assume that runs are fair, i.e. no task is starved forever by other running tasks. Fairness is commonly ensured by schedulers in multi-process systems. We also assume that runs are non-blocking, i.e. for each task that has not yet returned its answer, there is a service applicable to it or to one of its descendants.
Let be an artifact system, where . A run of on database instance over is an infinite sequence , where each is an instance of , , , , , , and for each . In addition, for each and task active in , there exists such that .
We denote by the set of runs of . Observe that all runs of are infinite. In a given run, the root task itself may have an infinite run, or other tasks may have infinite runs. However, if a task has an infinite run, then none of its ancestor tasks can make an internal transition or return (although they can still call other children tasks).
Because of the hierarchical structure of HAS*, and the locality of task specifications, the actions of independent tasks running concurrently can be arbitrarily interleaved. In order to express properties of HAS* in an intuitive manner, it will be useful to ignore such interleavings and focus on the local runs of each task, consisting of the transitions affecting the local variables and artifact relations of the task, as well as interactions with its children tasks. A local run of induced by is a subsequence of corresponding to transitions caused by ’s observable services (call these observable -transitions). starts from an opening service -transition and includes all subsequent observable -transitions up to the first occurrence of a closing service -transition (if any). See Appendix A for the formal definition. We denote by the set of local runs of induced by the run of , and .
2.1 Specifying properties of artifact systems
In this paper we focus on verifying temporal properties of local runs of tasks in an artifact system. For instance, in a task implementing an e-commerce application, we would like to specify properties such as:
If an order is taken and the ordered item is out of stock, then the item must be restocked before it is shipped.
In order to specify such temporal properties we use, as in previous work, an extension of LTL (linear-time temporal logic). LTL is propositional logic augmented with temporal operators such as G (always), F (eventually), X (next) and U (until) (e.g., see ). An LTL formula with propositions defines a property of sequences of truth assignments to . For example, says that always holds in the sequence, F says that will eventually hold, says that holds at least until holds, and says that whenever holds, must hold subsequently.
An LTL-FO property333The variant of LTL-FO used here differs from some previously defined in that the FO formulas interpreting propositions are quantifier-free. By slight abuse we use here the same name. of a task is obtained starting from an LTL formula using some set of propositions. Propositions in are interpreted as conditions over the variables of together with some additional global variables
, shared by different conditions and allowing to connect the states of the task at different moments in time. The global variables are universally quantified over the entire property. Recall thatconsists of the services observable in local runs of (including calls and returns from children tasks). A proposition indicates the application of service in a given transition.
LTL-FO is formally defined in Appendix A. We provide a flavor thereof using the example property . The property is of the form , which means if happens, then in what follows, will not happen until is true. Here says that the TakeOrder task returned with an out-of-stock item, states that the ShipItem task is called with the same item, and states that the service Restock is called to restock the item. Since the item mentioned in , and must be the same, the formula requires using a global variable to record the item ID. This yields the following LTL-FO property:
A correct specification can enforce simply by requiring in the pre-condition of that the item is in stock. One such pre-condition is , meaning that the item is in stock and the customer passed the credit check. However, in a similar specification where the test is performed within ShipItem (i.e. in the pre-conditions of all shipping internal services) instead of the opening service of ShipItem, the LTL-FO property is violated because ShipItem can be opened without first calling the Restock task. Our verifier would detect this error and produce a counter-example illustrating the violation.
We say that a local run of task satisfies , where , if is satisfied, for all valuations of in , by the sequence of truth assignments to induced by on . More precisely, let denote the snapshot of . For each , the truth value induced for in is the truth value of the condition in ; a proposition holds in if . A task satisfies if satisfies for every . Note that the database is fixed for each run, but may be different for different runs.
A classical result in model checking states that for every LTL formula , one can construct a finite-state automaton , called a Büchi automaton, that accepts precisely the infinite sequences of truth assignments to that satisfy . A Büchi automaton is syntactically just a finite-state automaton, which accepts an infinite word if it goes infinitely often through an accepting state [47, 44]. Here we are interested in evaluating LTL-FO formulas on both infinite and finite runs (infinite runs occur when a task runs forever). It is easily seen that for the obtained by the standard construction there is a subset of its states such that viewed as a classical finite-state automaton with final states accepts precisely the finite words that satisfy .
In  we consider a more complex logic for specifying properties of artifact systems, called Hierarchical LTL-FO (HLTL-FO). Intuitively, an HLTL-FO formula uses as building blocks LTL-FO formulas as above, acting on local runs of individual tasks, but can additionally recursively state HLTL-FO properties on runs resulting from calls to children tasks. As shown in , verification of HLTL-FO properties can be reduced to satisfiability of LTL-FO properties by individual tasks. Our implementation focuses on verification of LTL-FO properties of individual tasks. While this could be used as a building block for verifying complex HLTL-FO properties, verification of LTL-FO properties of individual tasks is in fact adequate in most practical situations we have encountered.
In this section we describe the implementation of VERIFAS. We begin with a brief review of the theory developed in  that is relevant to the implementation.
3.1 Review of the Theory
The decidability and complexity results of  can be extended to HAS* by adapting the proofs and techniques developed there. We can show the following.
Given a HAS* and an LTL-FO formula for a task in , it is decidable in expspace whether satisfies .
We outline informally the roadmap to verification developed in , which is the starting point for the implementation. Let be a HAS* and an LTL-FO formula for some task of . We would like to verify that every local run of satisfies . Since there are generally infinitely many such local runs due so the unbounded data domain, and each run can be infinite, an exhaustive search is impossible. This problem is addressed in  by developing a symbolic representation of local runs. Intuitively, the symbolic representation has two main components:
the isomorphism type of the artifact variables, describing symbolically the structure of the portion of the database reachable from the variables by navigating foreign keys
for each artifact relation and isomorphism type, the number of tuples in the relation that share that isomorphism type
Observe that because of (ii), the symbolic representation is not finite state. Indeed, (ii) requires maintaining a set of counters, which can grow unboundedly.
The heart of the proof in  is showing that it is sufficient to verify symbolic runs rather than actual runs. That is, for every LTL-FO formula , all local run of satisfy iff all symbolic local runs of satisfy . Then the verification algorithm checks that there is no symbolic local run of violating (so satisfying ). The algorithm relies on a reduction to (repeated444Repeated reachability is needed for infinite runs.
) state reachability in Vector Addition Systems with States (VASS). Intuitively, VASS are finite-state automata augmented with non-negative counters that can be incremented and decremented (but not tested for zero). This turns out to be sufficient to capture the information described above. The states of the VASS correspond to the isomorphism types of the artifact variables, combined with states of the Büchi automaton needed to check satisfaction of .
The above approach can be viewed as symbolically running the HAS* specification.
Consider the example in Section 2.
After the TakeOrder task is called and returned,
one possible local run of ProcessOrders might impose a set of constraints
onto the artifact tuple of ProcessOrders.
Now suppose the CheckCredit task is called.
The local run can make the choice that the customer has good credit.
Then when CheckCredit returns, the above set of constraints is updated with constraint
, which means that in the read-only database, the credit record referenced by via foreign key satisfies . Next, suppose the StoreOrder service is applied in ProcessOrders. Then we symbolically store the current set of constraints by increasing its corresponding counter by 1. The set of constraints of the artifact tuple is reset to as specified in the post-condition of StoreOrder.
Although decidability of verification can be shown as outlined above, implementation of an efficient verifier is challenging. The algorithm that directly translates the artifact specification and the LTL-FO property into VASS’s and checks (repeated) reachability is impractical because the resulting VASS can have exponentially many states and counters in the input size, and state-of-the-art VASS tools can only handle a small number of counters (100) . To mitigate the inefficiency, VERIFAS never generates the whole VASS but instead lazily computes the symbolic representations on-the-fly. Thus, it only generates reachable symbolic states, whose number is usually much smaller. In addition, isomorphism types in the symbolic representation are replaced by partial isomorphism types, which store only the subset of constraints on the variables imposed by the current run, leaving the rest unspecified. This representation is not only more compact, but also results in a significantly smaller search space in practice.
In the rest of the section, we first introduce our revised symbolic representation based on partial isomorphism types. Next, we review the classic Karp-Miller algorithm adapted to the symbolic version of HAS* for solving state reachability problems. Three specialized optimizations are introduced to improve the performance. In addition, we show that our algorithm with the optimizations can be extended to solve the repeated state reachability problems so that full LTL-FO verification of infinite runs can be carried out. For clarity, the exposition in this section focuses on specifications with a single task. The actual implementation extends these techniques to the full model with arbitrary number of tasks.
3.2 Partial Isomorphism Types
We start with our symbolic representation of local runs with partial isomorphism types. Intuitively, a partial isomorphism type captures the necessary constraints imposed by the current run on the current artifact tuple and the read-only database. We start by defining expressions, which denote variables, constants and navigation via foreign keys from id variables or attributes. An expression is either:
a constant occurring in or , or
a sequence , where is an id artifact variable or an id attribute of some artifact relation , is an attribute of where , and for each , , is a foreign key and is an attribute in the relation referenced by .
We denote by the set of all expressions. Note that the length of expressions is bounded because of the acyclicity of the foreign keys, so is finite.
We can now define partial isomorphism types.
A partial isomorphism type is an undirected graph over with each edge labeled by or , such that the equivalence relation over induced by the edges labeled with satisfies:
for every and every attribute , if and then , and
implies that and for every and , .
Intuitively, a partial isomorphism type keeps track of a set of “” and “” constraints and their implications among . Condition 1 guarantees satisfaction of the key and foreign key dependencies. Condition 2 guarantees that there is no contradiction among the -edges and the -edges. In addition, the connection between two expressions can also be “unknown” if they are not connected by an edge. The full isomorphism type can be viewed as a special case of partial isomorphism type where the undirected graph is complete. In the worst case, the total number of partial isomorphism types is no smaller than the number of full isomorphism types so using partial isomorphism types does not improve the complexity upper bound. In practice, however, since the number of constraints imposed by a run is likely to be small, using partial isomorphism types can greatly reduce the search space.
Figure 5 shows two partial isomorphism types (left) and (right), where is the only database relation and are 3 variables of type . Solid lines are -edges and dashed lines are -edges. In , is connected with so the edge is enforced by the key dependency. Missing edges between and indicate these connections are “unknown”. is a full isomorphism type, which requires the graph to be complete so , and all pairs between and must be connected by either or . The -edges between and are omitted in the figure for clarity.
We next define partial symbolic instances. Intuitively, a partial symbolic instance consists of a partial isomorphism type capturing the connections of the current tuple of , as well as, for the tuples present in the artifact relations, the represented isomorphism types and the count of tuples sharing .
A partial symbolic instance is a tuple where is a partial isomorphism type and is a vector of where each dimension of corresponds to a unique partial isomorphism type.
It turns out that most of the dimensions of equal 0 in practice, so in implementation we only materialize a list of those dimensions with positive counter values. We denote by the set .
Next, we define symbolic transitions among partial symbolic instances by applying internal services. First we need to define condition evaluation on partial isomorphism types. Given a partial isomorphism type , satisfaction of a condition in negation normal form555Negations are pushed down to leaf atoms. by , denoted , is defined as follows:
holds in iff for ,
for relation , holds in iff for every ,
holds in iff for some , and
Boolean combinations of conditions are standard.
Notice that might be due to missing edges in but not because of inconsistent edges, so it is possible to satisfy by filling in the missing edges. This is captured by the notion of extension. We call an extension of if and is consistent, meaning that the edges in do not imply any contradiction of (in)equalities. We denote by the set of all minimal extensions of such that . Intuitively, contains partial isomorphism types obtained by augmenting with a minimal set of constraints to satisfy .
A symbolic transition is defined informally as follows (the full definition can be found in Appendix A). To make a symbolic transition with a service from to , we first extend the partial isomorphism type to a new partial isomorphism type to satisfy the pre-condition . Then the constraints on the propagated variables are preserved by computing , the projection of onto . Intuitively, the projection keeps only the expressions headed by variables in and their connections. Finally, is obtained by extending to satisfy the post-condition . If is an insertion, then the counter that corresponds to the partial isomorphism type of the inserted tuple is incremented. If is a retrieval, then a partial isomorphism type with positive count is chosen nondeterministically and its count is decremented. The new partial isomorphism type is then extended with the constraints from . We denote by the set of possible successors of by taking one symbolic transition with any service .
Figure 6 shows an example of symbolic transition. The DB schema is that of Example 18. The variables are of type and a non-ID variable , with input variables . The applied service is . First, the pre-condition is evaluated so edge is added (top-middle). Edge is also added so that the partial isomorphism type remains valid. Then variables are propagated, so the edges related to or are removed (top-right). Next, we evaluate the post-condition so is added (bottom-right). Finally, a tuple from is retrieved and overwrites . Suppose the nondeterministically chosen contains a single edge (below the retrieve arrow). Then is decremented and is merged into the final partial isomorphism type (bottom left). Note that if contains an insertion of in instead of a retrieval, then the subgraph of (top-middle) projected to is inserted to . The corresponding counter in will be incremented by 1.
With symbolic transitions in place, verification works as follows. Informally, given a single-task HAS* and a LTL-FO property , one can check whether by constructing a new HAS* obtained by combining with conditions in and the Büchi automaton built from . We can show that deciding whether reduces to checking whether an accepting state of is repeatedly reachable in . Verification therefore amounts to solving the following problem:
(Symbolic Repeated Reachability, or SRR) Given a HAS* , an initial partial symbolic state , and a condition , is there a partial symbolic run of such that for every , , and ?
The condition above simply states that is in one of its accepting states.
3.3 The Classic Karp-Miller Algorithm
The SRR Problem defines an infinite search space due to the unbounded counter values, so reduction to finite-state model checking is not possible. Adapting the theory developed in  from symbolic representation based on isomorphism types to symbolic representation based on partial isomorphism types, we can show that the symbolic transitions defined in Section 3.2 can be modeled as a VASS whose states are the partial symbolic instances of Definition 19. Consequently, The SRR problem reduces to testing (repeated) state reachability in this VASS. The benefit of the new approach is that this VASS is likely to have much fewer states and counters than the one defined in , because our search materializes partial isomorphism types parsimoniously, by lazily expanding the current partial type using the (typically few) constraints of the symbolic transition to obtain a successor . The transition from to concisely represents all the transitions from full-type expansions of to full-type expansions of (exponentially many in the number of “unknown” connections in and ), which in the worst case would be individually explored by the algorithm of .
The VERIFAS implementation of the (repeated) state reachability is based on a series of optimizations to the classic Karp-Miller algorithm . We describe the original Karp-Miller algorithm first, addressing the optimizations subsequently.
The Karp-Miller algorithm constructs a finite representation which over-approximates the entire (potentially infinite) reachable VASS state space, called a “coverability set” of states . Any coverability set captures sufficient information about the global state space to support global reasoning tasks, including repeated reachability. In our context, the VASS states are the partial symbolic instances (PSIs) and a coverability set is a finite set of PSIs, each reachable from the initial PSI , such that for every reachable PSI , there exists with and . We say that covers , denoted . To represent counters that can increase forever, the coverability set also allows an extension of PSIs in which some of the counters can equal . Recall that the ordinal is a special constant where for all , and .
Since the coverability set is finite, we can effectively extract from it the reachable ’s that satisfy the condition (referring to the notation of the SRR problem). To test whether is repeatedly reachable, we can show that is repeatedly reachable iff is contained in a cycle consisting of only states in (proved in ). As a result, the repeatedly reachable ’s can be found by constructing the transition graph among and computing its strongly connected components. A partial isomorphism type is repeatedly reachable if its corresponding PSI is included in a component containing a non-trivial cycle.
The Karp-Miller algorithm searches for a coverability set by materializing a finite part of the (potentially infinite) VASS transition graph starting from the initial state and executing transitions, pruning transitions to states that are covered by already materialized states. The resulting transition subgraph is traditionally called the Karp-Miller tree (despite the fact that it is actually a DAG).
In the notation of the SRR problem, note that if at least one counter value strictly increases from to ( for some dimension ), then the sequence of transitions from to can repeat indefinitely, periodically reaching states with the same partial isomorphism type , but with ever-increasing associated counter values in dimension (there are infinitely many such states). In the limit, the counter value becomes so a coverability set must include a state with , covering these infinitely many states.
Finite construction of the tree is possible due to a special accelerate operation that skips directly to a state with -valued counters, avoiding the construction of the infinitely many states it covers. Adapted to our context, when the algorithm detects a path in the tree where , the accelerate operation replaces in the values of with for every where .
We outline the details in Algorithm 1, which outputs the Karp-Miller tree . We denote by the set of ancestors of in . Given a set of states and a state , the accelerate function is defined as where for every , if there exists such that , and . Otherwise, .
3.4 Optimization with Monotone Pruning
The original Karp-Miller algorithm is well-known to be inefficient in practice due to state explosion. To improve performance, various techniques have been proposed. The main technique we adopted in VERIFAS is based on pruning the Karp-Miller tree by monotonicity. Intuitively, when a new state is generated during the search, if there exists a visited state where , then can be discarded because for every state reachable from , there exists a reachable state starting from such that by applying the same sequence of transitions that leads to . For the same reason, if , then and its descendants can be pruned from the tree. However, correctness of pruning is sensitive to the order of application of these rules (for example, as illustrated in , application of the rules in a breadth-first order may lead to incompleteness). The problem of how to apply the rules without losing completeness was studied in [22, 39] and we adopt the approach in . More specifically, Algorithm 1 is extended by keeping track of a set of “active” states and adding the following changes:
Initialize with ;
In line 3, choose the state from ;
In line 5, is applied on ;
In line 8, is not added to if there exists such that ;
When is added to , remove from every state and its descendants for and is either active or not an ancestor of . Add to .
3.5 A Novel, More Aggressive Pruning
We generalize the comparison relation of partial symbolic instances to achieve more aggressive pruning of the explored transitions. The novel comparison is based on the insight that a state can be pruned in favor of as long as every partial isomorphism type reachable from is also reachable from . So can be pruned by if is “less restrictive” than (or implies ), and for every occurrence of in , there exists a corresponding occurrence of in such that is “less restrictive” than . Formally, given partial isomorphism types and , implie