1 Introduction

This paper is concerned with logic-based modeling and automated reasoning for estimating the current state of a system as it evolves over time. The main motivation is situational awareness [12], which requires the ability to understand and explain a system’s state, at any time, and at a level that matters to the user, even if only partial or incorrect information about the external events leading to that state is available. In a supply chain context, for example, one cannot expect that events are reported correctly and in a timely manner. Sensors may fail, transmission channels are laggy, reports exist only in paper form, not every player is willing to share information, etc. Because of that, it is often impossible to know with full certainty the actual state of the system. The paper addresses this problem and proposes to instead derive a set of plausible candidate states as an approximation of ground truth. The states may include consequences relevant for situational awareness, e.g., that a shipment will be late. A human operator may then make decisions or provide additional details, this way closing the loop.

The plausible candidate states are represented as models of a logical specification and a given a set of external timestamped events. The proposed modeling paradigm is logic programming, and models are computed in a bottom-up way. It adopts notions of stratification, default negation and a possible model semantics for its (disjunctive) program rules. Stratification is in terms of event time, with increasing time horizons for anytime reasoning; default negation is needed to reason in absence of information such as event reports; disjunctions are needed to derive alternate candidate states. In order to deal with less-than-perfect event data, the modeling language features a novel model revision operator that allows the programmer to formulate conditions under which a model computation with a corrected set of events should be attempted in an otherwise inconsistent state. The following informal overview illustrates these features.

1.1 Main Ideas and Design Rationale

A model, or program, is comprised of a set of rules of the form \( head \leftarrow body \). The \( head \) can be a non-empty disjunction of atoms, or a \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}\) head. The former rules open the solution (models) space for a fixed set of external events, while \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}\) head rules limit it and specify amended event sets for new solution attempts. The \( body \) is a conjunction of atoms and negated (via “”) conjunctions of atoms. Negation is “default negation”, i.e., a closed world assumption is in place for evaluating the latter. Rules may contain first-order variables and must be range restricted. This guarantees that only ground heads can be derived from ground facts when a rule is evaluated in a bottom-up way. Our notion of range restriction is somewhat non-standard, though, and permits extra variables inside negation. These variables are implicitly existentially quantified (“\({{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\ \exists \, x \ldots \) ”).

We need, however, syntactic restrictions that enforce stratification in terms of “time”. This entails that rule evaluation does not depend on facts from the future. In fact, this is a reasonable assumption for situational awareness, whose any-time character requires to understand the current situation based on the available information up to now only. Technically, every atom must have a dedicated “time” argument, a \(\mathbb {N}\)-sorted variable, which, together, with earlier-than constraints (via “<” or “\(\le \)”) enforces stratification. The details of that will have to wait until later (Definition 1). An example rule is

(1)

It could say “if x gets up at time t and didn’t have a meal in 6 hours prior then x is hungry or thirsty at t, or both”. A set of facts, say, then entails . Notice that in the relevant rule instance the negated body element is satisfied by the facts (using the closed-world assumption), as for the only relevant -instance the arithmetic constraint is false. The possible model semantics [26], which we adopt, interprets disjunctions (also) inclusively. Each resulting case , and together with the facts yields a possible model.

Stratification in terms of time makes default negation with existentially quantified variables possible; no need to look into the future. We impose a secondary kind of stratification that also makes it more efficient. It rests on distinguishing two types of atoms: EDB atoms and IDB atoms (extensional/intensional database, respectively). EDB atoms are for external events, the given facts, and IDB atoms are for derived facts. A disjunctive \( head \) can contain IDB atoms only. Now, an IDB atom within a negation has to be strictly earlier (<) than the head, while an EDB atom within a negation can be non-strictly earlier (\(\le \)). This make sure that a truth value for a negated expression cannot change later, in the course of rule evaluation in increasing time order. The rule (1) above is stratified if is EDB, otherwise “\(s \le t\)” would have to be “\(s < t\)”. A rule like cannot be stratified.

Rules with \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}\) heads enable the programmer to specify when a (partial) model candidate is unsatisfiable and to say how to potentially fix this situation. A rule of the form \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}() \leftarrow body \) without arguments to \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}\) is a usual integrity constraint, e.g.:

(2)

The rule (2) rejects that x is both hungry and has eaten within the last 4 hours. Together with rule (1) and the EDB one obtains the sole possible model .

The second usage is of the form \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}(+a, -b) \leftarrow body \), where a and b are EDB atoms with timestamps not in the future. When the rule body is satisfied, the model computation restarts with a added and b removed from the EDB. For example, the rule

(3)

rejects a model candidate where x has eaten within one hour before getting up. The rule correspondingly removes the event from the EDB and the model computation restarts. One could further add

(4)

to (1)–(3) as an alternative to fix the problem. The principle is that as soon as the earliest \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}\) head is derived, the model candidate at that time is given up. Then alternate model computations are started for all \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}\) heads derivable for that time. Later times are not considered. In the example, thus, both restarts prescribed by rules (3) and (4) are tried.

1.2 Related Work and Novelty

Assigning models to logic programs as their intended meaning has been studied for decades. We only mention the stable models semantics [14, 18], its extension for disjunctive programs [11, 15], and the possible model semantics [26, 27] as the most relevant in the following discussion. Both ascribe meaning to a given program in terms of minimal models, but differ in the way disjunctive rule heads are interpreted (exclusive vs. inclusive, respectively).

Most reasoning tasks around stable models are rather complex, e.g., model existence for propositional disjunctive programs is \(\Sigma ^P_2\)-complete [10]. This complexity translates into generate-and-test algorithms even without default negation. For instance, the tableau calculus in [22] for negation-free programs generates in its branches model candidates, whose minimality need to be tested by a subsequent theorem prover call. Another need for generate-and-test algorithms for stable models comes from default negation. In the general case, these algorithms need to guess a stable model candidate and verify its minimality for a certain negation-free program obtained by simplification with this candidate, the Gelfond-Lifschitz transformation.

In contrast, our approach avoids the intricacies of generate-and-test algorithms. This is achieved by using the possible model semantics [26] and a specific concept of stratification for dealing with default negation. The latter fits in the framework of local stratification [25]. A similar concept of stratification by time has been employed for expressing greedy algorithms in Datalog [30]). The usual stratified case, by predicates, but without quantification within negation was already been considered in [26].

As indicated in Sect. 1.1 our language features a \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}\) operator for model revision. This feature is the one that possible stands out most among the other mentioned. A rule applied to the facts derives the model . This cannot be achieved without belief revision, as given facts have to be satisfied.

Belief revision [1, 24] is the process of changing beliefs to take into account a new piece of information. It has also been studied extensively in a logic programming context and in a general way. For instance, Schwind and Inoue [29] consider the problem of revision by a program in a rather expressive setting, generalized logic programs equipped with stable model semantics. The perhaps closest approach to ours are the revision specifications of [19, 20]. Revision programs generalize logic programming with stable model semantics by an explicit deletion operator. Each revised model is obtained from the initial interpretation by means of insertions and deletions specified by a Gelfond-Lifschitz type reduced program. In that way, our approach is related, but simpler, as it revises only the EDB and does not require a generate-and-test algorithm. On the other hand, the semantics of our revisions takes timestamps into account, so that intended revisions are only those that are derivable “now”.

The focus on the paper is not on situational awareness as such. We merely mention that the problem has attracted interest from a logical perspective. In earlier work [2] we proposed bottom-up model computation with a Hyper Tableaux prover [4, 23] as a component for data aggregation. In a related context of conformance checking, the authors of [7] propose if-then rules for validating process execution traces by means of a Prolog interpreter. Other approaches for conformance checking include planning [9] and diagnosis of discrete dynamical systems [8, 21].

To sum up, the main novelty of our approach lies in the combination of the possible model semantics with specific concepts of stratification and model revision. The combination is designed to enable simple fixpoint algorithms that are sound and complete for a not too complicated declarative semantics. This is the main theoretical contribution.

On the practical side we offer a (publicly available) implementation of our calculus, as a shallow embedding into the Scala programming language. Somewhat related, a shallow embedding into Scala has been used for monitoring event streams over Allen’s temporal interval logic [17]. Yet, it is an uncommon implementation technique for automated reasoning systems. The practical advantages are described in Sect. 6.

2 Preliminaries

We assume the reader is familiar with basic notions of first-order logic and answer set programming. See [6] and [13], respectively, for introductory texts.

A first-order logic signature \(\Sigma = \Sigma _P \uplus \Sigma _F\) is comprised of predicate symbols \(\Sigma _P\) and function symbols \(\Sigma _F\) of fixed arities. We assume \(\mathbb {N}\subseteq \Sigma _F\), i.e., the natural numbers are also constants of the logical language, and that \(\Sigma _P\) contains the arithmetic predicate symbols \(\Sigma _\mathbb {N}= \{<, \le , =, \ne \}\). The ordinary predicate symbols are \(\Sigma _P \setminus \Sigma _\mathbb {N}\). Let \(\mathcal{X}\) be a countably infinite set of variables. Instead of introducing a two-sorted signature we assume informally that all terms and formulas over \(\Sigma \) and \(\mathcal{X}\) are built in a sorted way.

The letters s and t usually stand for terms, x and y stand for variables, and p and q for ordinary predicate symbols. We speak of ordinary atoms and arithmetic atoms depending on whether the predicate symbol is ordinary or arithmetic, respectively. For a set A of atoms let \( ord (A)\) be the set of all ordinary atoms in A.

Intuitively, \(\mathbb {N}\) represents timestamps (points in time), and < and \(\le \) stand for the strict and non-strict earlier-than relationships, respectively. We assume every ordinary predicate symbol has arity \(\ge \) 1 and its, say, first argument ranges over \(\mathbb {N}\). For any ordinary atom \(a = p(t_1,\ldots ,t_n)\) let \( time (a) = t_1\) be its timestamp. The function symbols \(\Sigma _F\) may contain arithmetic operations as needed to compute with timestamps, but \(\Sigma _F\) may not contain uninterpreted operators with \(\mathbb {N}\) as the result sort.

We assume the ordinary predicate symbols are partitioned as \(\Sigma _P\setminus \Sigma _\mathbb {N}= \Sigma _\mathrm {EDB}\uplus \Sigma _\mathrm {IDB}\). The symbols in \(\Sigma _\mathrm {EDB}\) are called extensional database (EDB) predicates, and the symbols in \(\Sigma _\mathrm {IDB}\) are the intensional database (IDB) predicates. An EDB is a finite set of ground \(\Sigma _\mathrm {EDB}\)-atoms, and an IDB is a finite set of ground \(\Sigma _\mathrm {IDB}\)-atoms. We may think of an EDB as a timestamped sequence of external events, and an IDB as higher-level conclusions derived from that EDB. Below we will exploit this distinction for computing models in a stratified way and for defining a model revision operator.

As usual, a substitution \(\sigma \) is a mapping from the variables to terms. A substitution is identified with its homomorphic extension to terms. Substitution application is written postfix, i.e., we write \(t\sigma \) instead of \(\sigma (t)\). The domain of \(\sigma \) is the set \( dom (\sigma ) = \{ x \in \mathcal{X}\mid x\sigma \ne x \}\) and is always assumed to be finite.

When z is a term, an atom, a sequence, or a set of those, let \( var (z)\) denote the set of variables occurring in z. We say that z is ground if \( var (z) = \emptyset \). A substitution \(\gamma \) is a grounding substitution for z iff \(z\gamma \) is ground. In this case \(z\gamma \) is also called a ground instance of z (via \(\gamma \)). Let \( gnd (z)\) denote the set of all ground instances of z.

3 Stratified Programs

We are now in a position to define our main modeling tool, a variation on if-then rules as popularized in the area of disjunctive logic programming.

A positive body is a list \(\vec {b} = b_1,\ldots ,b_k\) of atoms with \(k \ge 0\). If \(k = 0\) then \(\vec {b}\) is empty otherwise it is non-empty. (The list represents a conjunction.) A negative body literal is an expression of the form \({{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}\), where \(\vec {b}\) is a non-empty positive body. A body is a list \(b_1,\ldots ,b_k, {{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_{k+1}, \ldots ,{{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_n\) comprised of a (possibly empty) positive body and (possibly zero) negative body literals. It is variable free if \( var (b_1,\ldots ,b_k) = \emptyset \). A head is one of the following:

  1. (a)

    an ordinary head: a disjunction \(h_1 \vee \cdots \vee h_m\) of IDB atoms, for some \(m \ge 1\), or

  2. (b)

    a fail head: an expression of the form \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}(\vec {e})\) where \(\vec {e} = \pm _1\, e_1,\ldots ,\pm _k\, e_k\), for some \(k \ge 0\), EDB atoms \(e_i\) and \(\pm _i \in \{+,-\}\). If \(k=0\) then \(\vec {e}\) is the empty sequence \(\varepsilon \), and \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}(\epsilon )\) is usually written as \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}()\).

A rule consist of a head H and a body and is commonly written as an implication

(5)

By an ordinary rule (fail rule) we mean a rule with an ordinary head (fail head), respectively. A fail set is a (possibly empty) set of ground fail heads.

Let r be a rule (5) and \(\vec {b} = b_1,\ldots ,b_k\) its positive body. We say that r is variable free iff \( var (H) \cup var (\vec {b}) = \emptyset \). This notion of variable-freeness is justified by the fact that the extra variables \( var (\vec {b}_i) \setminus var (\vec {b})\) in the negative body literals \({{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_i\) are implicitly existentially quantified, see Definition 5 below. We say that \(r'\) is a variable free instance of r via \(\sigma \) iff \(r' = r\sigma \) is variable free and \( dom (\sigma ) = var (H) \cup var (\vec {b})\). Notice that \(\sigma \) must not act on the extra variables as these are shielded by quantification.

A program is a set of rules. It is variable free if all of its rules are. Semantically, every program R stands for the (possibly infinite) variable free program \( vfinst (R)\) that is obtained by taking all variable free instances of all rules in R.

The rules are to be evaluated in a bottom-up way. If a current model candidate satisfies a rule body then its head needs evaluation. An ordinary rule extends the current model according to the possible model semantics as explained below and a fail rule rejects the current model. If a fail rule’s head is \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}()\) it acts like a traditional rule with an empty head, as an integrity constraint. If the argument list \(\vec {e}\) is non-empty the fail rule “fixes” the current EDB by adding (“\(+\)”) or removing (“−”) EDB atoms and starting a new model computation.

In order to admit effective model computation our rules will be stratified. Stratification means range-restrictedness and other restrictions on variables and negation.

Definition 1 (Stratified rule)

Let r be a rule (5) with positive body \(\vec {b} = b_1,\ldots ,b_k\) and y be a variable. The rule r is stratified wrt. y if there is a \(b \in \vec {b}\) such that \( time (b) = y\) and the following holds:

  1. (i)

    \( var (\vec {b}) \subseteq var ( ord (\vec {b}))\),

  2. (ii)

    for every ordinary atom \(b \in \vec {b}\), \( time (b) = y\) or \( time (b) = x\) and \(x \lhd y \in \vec {b}\) for some variable x and \(\lhd \in \{<, \le \}\),

  3. (iii)

    every negative body literal \({{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_{k+1},\ldots ,{{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_n\) is stratified, and

  4. (iv)

    the head H is stratified.

In the above, a negative body literal \({{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_i\) is stratified if the following holds:

  1. (i)

    \( var (\vec {b}_i) \setminus var (\vec {b}) \subseteq var ( ord (\vec {b}_i))\),

  2. (ii)

    for every EDB atom \(b \in \vec {b}_i\), \( time (b) = y\) or \( time (b) = x\) and \(x \lhd y \in \vec {b}_i\) for some variable x and \(\lhd \in \{<, \le \}\), and

  3. (iii)

    for every IDB atom \(b \in \vec {b}_i\), \( time (b) = x\) and \(x < y \in \vec {b}_i\) for some variable x.

The head H is stratified if the following holds:

  1. (i)

    \( var (H) \subseteq var ( ord (\vec {b}))\),

  2. (ii)

    if H is an ordinary head \(h_1 \vee \cdots \vee h_m\) then \( time (h_1) = \cdots = time (h_m) = y\).

  3. (iii)

    if H is a fail head \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}(\vec {e})\) then for all \(\pm e \in \vec {e}\), \( time (e)\) is an arithmetic expression and \( time (e) \lhd y \in \vec {b}\), for some \(\lhd \in \{<, \le \}\).

A rule is stratified if it is stratified wrt. some variable y. A program is stratified if each of its rules is stratified.Footnote 1

Definition 1 expresses conditions on rules in terms of time-restrictedness and range-restrictedness. The variable y stands for the latest of all timestamps among all timestamps of the ordinary atoms in the rule body. This is made sure by constraints \(x \lhd y\) in the various parts of the definition where \(\lhd \in \{ <, \le \}\). More precisely, ordinary atoms in the positive body are timestamped “\(\le \)”; ordinary heads are timestamped “y” so that no literals timestamped in the past can be inserted into the model (this would defy stratification); and restarts can modify only the past. For the ordinary atoms in negative body literals we distinguish between EDB and IDB atoms. EDB atoms cannot be derived in heads of rules, which affords “\(\le \)”, whereas IDB atoms must be “<”.

Fig. 1.
figure 1

Supply chain program. See Example 2 for explanations.

The remaining conditions force range-restrictedness. In the first part, condition (i) says that every variable in a positive body atom appears also in some ordinary positive atom; similarly for condition (i) for heads. Condition (i) for negative body atoms says that every extra variable in a negative body atom appears also in some of its ordinary body atoms. Together these conditions make sure that matching a rule’s ordinary atoms against a ground candidate model always removes all variables. This way, all arithmetic expressions can be evaluated and only ground heads can be derived.

Example 1 (Stratified rule)

Let and . The rule is stratified wrt. \(x_3\) (). It collects in the timestamps between consecutive -events. For example, given the set , the rule when applied exhaustively derives , and but not, e.g, . The extra variable \(x_2\) in its negative body literal is implicitly existentially quantified.   \(\square \)

Example 2 (Supply chain)

The program in Fig. 1 illustrates a possible use of our approach in a supply chain application. It is written in the concrete input syntax of our implementation, the Fusemate system (Sect. 6).

The signature is and . A fluent expresses that at the given an object is loaded into a container , similarly for . A fluent says that at the given an object is inside the container .

With this interpretation, the rules (1) and (2) for the relation should be obvious. Rule (3) is a frame axiom for the relation. That is, it states when an -fluent carries over to the next timestamp: an object remains in a container if neither it nor a container containing it is unloaded from the container. The body atom holds true if is the most recent timestamp preceding . The relation is “built-in” into Fusemate for convenience.

Rule (4) fixes the problem of a “missing” unloading event by inserting one into the EDB at a speculated time . This rule will become clearer in Example 3 below, where we discuss the program in conjunction with a concrete EDB.

Rule (5) says that only items that are in a container can be unloaded in the next step. Rule (6) demands loading prior to unloading. The other rules will also be discussed below.   \(\square \)

4 Semantics

The possible model semantics [26, 27] associates to a disjunctive program sets of possible facts that might have been true in the actual world. (This is already a good fit for situational awareness.) We extend it to our stratified programs with fail rules.

Let \(\mathrm Th(\Sigma _\mathbb {N})\) be the set of all ground time atoms that are true in the standard model of natural number arithmetic. For a set A of ground ordinary atoms define \(\mathcal{I}(A) = A \cup \mathrm Th(\Sigma _\mathbb {N})\) which represents the Herbrand \(\Sigma \)-interpretation that assigns true to a ground atom a if and only if \(a \in \mathcal{I}(A)\).

Definition 2 (Rule semantics)

A set A of ground ordinary atoms satisfies a variable free body \(B = b_1,\ldots ,b_k, {{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_{k+1}, \ldots ,{{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_n\), written as \(A \models B\), if \(\{ b_1,\ldots , b_k \} \subseteq \mathcal{I}(A)\) and \(( gnd (\vec {b}_{k+1}) \cup \cdots \cup gnd (\vec {b}_{n})) \cap \mathcal{I}(A) = \emptyset \).

The set A satisfies a variable free ordinary rule \(h_1 \vee \cdots \vee h_m \leftarrow B\) if \(A \models B\) entails \(\{h_1, \ldots , h_m\}\cap \mathcal{I}(A) \ne \emptyset \). It is a model of a set R of variable free ordinary rules, written as \(A \models R\), iff it satisfies every rule in R.

The fail set of A and a set of variable free fail rules R is the set \(F = \{ {{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}(\vec {e}) \mid \text { there is a rule } {{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}(\vec {e}) \leftarrow B \in R \text { such that } A \models B \}\). This is written as \(A \models _R^{{{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}} F\).

Satisfaction of a variable free body according to Definition 2 is equivalent to satisfaction of the first-order logic formula \(b_1 \wedge \cdots \wedge b_k \wedge \lnot (\exists \, \vec {x}_{k+1} . \wedge \! \vec {b}_{k+1}) \wedge \cdots \wedge \lnot (\exists \, \vec {x}_{n} .\ \wedge \! \vec {b}_{n})\) in the interpretation \(\mathcal{I}(A)\), where \(\vec {x}_j = var (\vec {b}_j)\), for all \(j = k+1,\ldots ,n\). .

Definition 2 is, in fact, somewhat more general than needed for defining the possible models semantics of logic programs. Possible models interpret disjunctive heads inclusively, in all possible ways. This is expressed in the following definition.

Definition 3

(Split program [26, 27]). Let R be a variable free program. A split program of R is any program obtained from R by replacing every ordinary rule \(h_1 \vee \cdots \vee h_m \leftarrow B\) by the normal ordinary rules (called split rules) \(h \leftarrow B\), for every \(h \in \mathcal{H}\), where \(\mathcal{H}\) is some non-empty subset of \(\{h_1,\ldots ,h_m\}\).

In [26, 27], Sakama et al. define the possible model semantics (also) for disjunctive programs without negation. Our stratified case admits a similar definition.

For any program R let \(R^+\) (\(R^-\)) be the set of all ordinary (fail) rules of R, respectively.

Definition 4 (Satisfication of stratified programs)

Let P be a stratified program, F a fail set, E an EBD and I an IDB. We write \((E,I) \models _P^{{{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}} F\) if there is a split program S of \( vfinst (P)\) such that all of the following hold:

  1. (i)

    \(E \cup I \models S^+\) \(\qquad\qquad\qquad\qquad (E \cup I\) is a model of the ordinary rules)

  2. (ii)

    \(E \cup J \models S^+\) for no \(J \subsetneq I \qquad\qquad\qquad\qquad\qquad\qquad \) (I is minimal)

  3. (iii)

    \(E \cup I \models _{S^-}^{{{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}} F \qquad\qquad\qquad\qquad\qquad\ \ \) (F is the triggered fail heads)

If \(F = \emptyset \) then we say that (EI) satifies P and writte \((E,I) \models P\).

As an example (without time), if \(E = \{ p \}\) and \(R = \{ q \vee r \leftarrow p,\ q \leftarrow r,\ s \leftarrow s \}\) then \(\{ p,q,s\} \models R\), but only \((\{p\}, \{q\})\) and \((\{p\}, \{r, q\})\) satisfy R in the sense of Definition 4.

The purpose of a program P is to compute all extensions (EI) of a given EDB E that satisfy P. For failed such attempts, P also specifies ways to revise E, if any, as early as possible, leading to new tries. The following Definition 5 make this precise.

For a ground fail head \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}(\vec {e})\) and ground EDB E let \( upd (E,\vec {e})\) be the EDB obtained from E by first adding all EDB atoms e such that \(\vec {e}\) contains the expression \(+ e\), and then deleting all all EDB atoms e such that \(\vec {e}\) contains \(- e\). For any set A of ground ordinary atoms and \(t \in \mathbb {N}\) let \(A_{\le t} = \{ a \in A \mid time (a) \le t\}\); analogously for \(A_{< t}\).

Definition 5 (Possible models of stratified programs)

Let P be a stratified program and \(E_{\mathsf {init}}\) an EDB. Let \(\mathcal{E}\) be the smallest set of EDBs containing \(E_{\mathsf {init}}\) and satisfying, for all \(E \in \mathcal{E}\), timestamps t in E, IDBs I and fail sets F:

If \((E_{\le t},I) \models _P^{{{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}} F\) and there is no \(J \subseteq I\) and \(G \ne \emptyset \) such that \((E_{<t},J) \models _P^{{{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}} G\)

then \( \{ upd (E, \vec {e}) \mid {{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}(\vec {e}) \in F \text { and } \vec {e} \ne \varepsilon \} \subseteq \mathcal{E}\).

The set \(\mathcal{E}\) is called the restart EDBs induced by P and \(E_{\mathsf {init}}\). Any pair (EI) such that \(E \in \mathcal{E}\) and \((E, I) \models P\) is called a possible model of P and \(E_{\mathsf {init}}\), written as \((E_{\mathsf {init}},E, I) \models P\). Let \( mods _P(E_{\mathsf {init}}) = \{ (E,I) \mid (E_{\mathsf {init}},E, I) \models P\}\) be all such possible models.

The set \(\mathcal{E}\) in Definition 5 contains a restart EDB E apart from \(E_{\mathsf {init}}\) if and only if E is obtained from some earliest time fail set F from another restart EDB in \(\mathcal{E}\). This excludes fail sets that may otherwise be additionally derivable at a later time. This was a design decision in support of “anytime” reasoning, for not having to consider future events.

Example 3

(Supply chain, Example 2 continued). Consider the following EDB \(E_{\mathsf {init}}\) consisting of loading and unloading events:

The intuitive meaning of the atoms between times 10 and 40 should be obvious. All what is reported at time 60 is that apples are unloaded from the pallet. However, this is suspicious from a (practical) completeness and consistency perspective. First, it can be alleged that some unloading events went under unreported. Before an item (apples) can be unloaded from a pallet that was loaded earlier into a container, the pallet needs to be unloaded from the container first, and that container must have been unloaded from the ship. Such reports are missing. Second, loading of tomatoes does not go together well with the unloading of apples later. This could be a reporting inconsistency or a reporting incompleteness if indeed apples were (also) loaded earlier.

All these plausible explanations are provided by \((E_1,I_1)\), \((E_2,I_2)\), and \((E_3,I_3)\), the three possible models of \(E_{\mathsf {init}}\) and the program in Fig. 1. For space reasons we list only their EDB components, which are as follows:

In each of these models, the missing unloading events and are added by repeated application of rule (4). Generally speaking, rule (4) inserts an of the “containing container” the object to be unloaded from is in. The rules (7) – (9) all fix the “unloading apples vs. loading tomatoes” problem. Rule (7) leads to \((E_1, I_1)\), rule (8) leads to \((E_2, I_2)\), and rule (9) leads to \((E_3, I_3)\). Each of these rules tests whether an object (apples) is swappable with another object (tomatoes) for the purpose of model revision, which is the case if the relation says so. Notice that if \(E_{\mathsf {init}}\) had, say instead of then none of the rules (7) – (9) is applicable and no possible model exists.    \(\square \)

5 Model Computation

This section introduces our calculus for computing possible models of stratified programs. It borrows some terminology from tableau calculi. A path p is a triple (EIt) where E is an EDB, I is an IDB and \(t \in \mathbb {N}\) is a timestamp. Intuitively, p represents the interpretation \(\mathcal{I}((E \cup I)_{\le t})\). An initial path is of the form \((E,\emptyset ,0)\). A tableau is a finite set of paths.Footnote 2

Let \(B = b_1,\ldots ,b_k, {{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_{k+1}, \ldots ,{{\,\mathrm{{\mathbf {\mathsf{{not}}}}}\,}}\vec {b}_n\) be the body of a variable free stratified rule and A a set of ground ordinary atoms. A substitution \(\sigma \) with \( dom (\sigma ) = var (b_1,\ldots ,b_k)\) is a body matcher for B on A, written as \((B,\sigma ) \sqsubseteq A\), if the following holds:

  1. (i)

    \(\{ b_1\sigma ,\ldots ,b_k\sigma \} \subseteq A \cup \mathrm Th(\Sigma _\mathbb {N})\), and

  2. (ii)

    for no \(i = k+1 \ldots n\) there is a grounding substitution \(\gamma \) for \(\vec {b}_i\sigma \) such that \(\vec {b}_i\sigma \gamma \subseteq A \cup \mathrm Th(\Sigma _\mathbb {N})\).

Note 1 (Computing body matchers)

The definition of body matchers only applies to bodies of stratified rules. It is easy to see that a body matcher \(\sigma \), if any exists, can be found by computing a simultaneous matching substitution \(\sigma \) for the ordinary atoms among \(b_1,\ldots ,b_k\) to A. Similarly for the substitution \(\gamma \) in condition (ii). Furthermore, stratification guarantees that all arithmetic atoms for testing conditions (i) and (ii) are necessarily ground and hence can be evaluated.   \(\square \)

An inference rule is a schematic expression of the form \(p \Rightarrow p_1,\ldots ,p_k\) where p and \(p_j\) are paths, for all \(1 \le j \le k\), where \(k \ge 0\). It means that the premise p is to be replaced by the conclusions \(p_1,\ldots ,p_k\). An inference is an instance of an inference rule.

In the following, P is a stratified program and \(\sigma \) is a substitution such that \(r\sigma \) is a variable free instance of a rule r that is clear from the context.

  • \(\mathsf {Ext}\): \((E,I,t)\ \Rightarrow \ (E,I\cup \mathcal{H}_1,t),\ \ldots ,\ (E,I\cup \mathcal{H}_k,t)\) if P contains an ordinary rule \(h_1 \vee \cdots \vee h_k \leftarrow B\) such that \(\{ \mathcal{H}_1, \ldots , \mathcal{H}_k \} = \{ \mathcal{H}\mid (B,\sigma ) \sqsubseteq (E \cup I)_{\le t} \text{ and } \emptyset \subsetneq \mathcal{H}\subseteq \{h_1\sigma ,\ldots ,h_k\sigma \} \}\)

  • \(\mathsf {Restart}\): \((E,I,t)\ \Rightarrow \ ( upd (E,\vec {e}_1) ,\emptyset , 0), \ \ldots ,\ ( upd (E,\vec {e}_k) ,\emptyset , 0)\) if \(k \ge 1\) and \(\{ \vec {e}_1,\ldots ,\vec {e}_k \} = \{ \vec {e}\sigma \mid {{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}(\vec {e}) \leftarrow B \in P,\ \vec {e} \ne \varepsilon \text { and } (B,\sigma ) \sqsubseteq (E\cup I)_{\le t}\}\).

  • \(\mathsf {Fail}\): \((E,I,t)\ \Rightarrow \ \) if P contains a rule \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}() \leftarrow B\) and there is a \(\sigma \) such that \((B,\sigma ) \sqsubseteq (E\cup I)_{\le t}\).

  • \(\mathsf {Jump}\): \((E,I,t) \ \Rightarrow \ (E,I,s)\) if s is the least timestamp in E with \(t < s\).

The \(\mathsf {Ext}\) rule extends I to satisfy all split rules for each case \(\mathcal{H}\) of some instance of an ordinary rule in P whose body is satisfied by \((E \cup I)_{\le t}\); \(\mathsf {Restart}\) replaces the current path with all initial paths as per the non-empty fail rules after \(\mathsf {Ext}\) is exhausted; \(\mathsf {Fail}\) also terminates the current path but is to be applied only if \(\mathsf {Restart}\) doesn’t; \(\mathsf {Jump}\) advances the current time bound t. The following formalizes this intuition.

An initial path \((E,\emptyset ,0)\) is new wrt. a tableau T iff there is no I and no t such that \((E,I,t) \in T\). Let \(E_{\mathsf {init}}\) be an input EDB. A derivation D (from \(E_{\mathsf {init}}\) and P) is a sequence \((T)_{i\ge 0}\) of tableaus \(D = (T_0 = \{ (E_{\mathsf {init}}, \emptyset , 0) \}), T_1, T_2,\ldots \) such that, for all \(i \ge 0\), there is a selected path \(p \in T_i\) and \(T_{i+1} = (T_i \setminus \{p\}) \cup \{p_1,\ldots ,p_k\}\) where:

  1. (a)

    \(p \Rightarrow p_1,\ldots ,p_k\) by \(\mathsf {Ext}\) and \(\{ p_1,\ldots , p_k \} \not \subseteq T_i\),

  2. (b)

    \(p \Rightarrow q_1,\ldots ,q_m\) by \(\mathsf {Restart}\) and ,

  3. (c)

    \(p \Rightarrow \quad \) by \(\mathsf {Fail}\) and \(k = 0\), or

  4. (d)

    \(p \Rightarrow p_1\) by \(\mathsf {Jump}\) and \(k = 1\).

In addition, the inference rules must be prioritized in this order. That is, if \(T_{i+1}\) is obtained from \(T_i\) by, say, case (c) , then there is no tableau that can be obtained from \(T_i\) by case (a) or case (b) with the same selected path p; analogously for the other cases.

The derivation D is exhausted if it is finite and no inference rule is applicable to its final tableau \(T_n\), for no \(p \in T_n\). In this case the computed models of D is the set .

Figure 2 is a graphical illustration of a derivation and its computed models.

Fig. 2.
figure 2

Illustration of a hypothetical derivation. The root of each sub-tableau is labeled with the EDB in that sub-derivation. The first sub-tableau has two \(\mathsf {Restart}\) inferences, leading to the second and third sub-tableau, where \(E_1 = upd (E_{\mathsf {init}}, \vec {e}_0^0)\), \(E_2 = upd (E_{\mathsf {init}}, \vec {e}_0^1)\). The isolated \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}()\)s do not cause a \(\mathsf {Restart}\), they cause \(\mathsf {Fail}\). The computed models are \((E_{\mathsf {init}}, I_0^0)\), \((E_{\mathsf {init}}, I_0^1)\), \((E_1, I_1^0)\), etc.

Theorem 1 (Soundness and completeness)

Assume a signature \(\Sigma \) without k-ary function symbol, for \(k >0\). Let P be a stratified program and \(E_{\mathsf {init}}\) an EDB. Assume an exhausted derivation D from \(E_{\mathsf {init}}\) and P. Then \(\mathcal{M}(D) = mods _P(E_{\mathsf {init}})\).

Proof

(Sketch) Let \(T_n\) be the final tableau of D. For soundness, assume \(\mathcal{M}(D) \ne \emptyset \) and chose any \((E,I) \in \mathcal{M}(D)\) arbitrary. That is, \((E,I,t) \in T_n\), for some t. We have to show \((E,I) \in mods _P(E_{\mathsf {init}})\), equivalently \((E_{\mathsf {init}}, E, I) \models P\).

The EDB E is either \(E_{\mathsf {init}}\) or derived from \(E_{\mathsf {init}}\) through, say, \(k > 0\) intermediate EDBs by \(\mathsf {Restart}\)s. By induction on k one can show that, on the semantic side, E is a restart induced by P and \(E_{\mathsf {init}}\), i.e., \(E \in \mathcal{E}\) in Definition 5. This follows from the definition of derivations. In particular, the earliest-time requirement in Definition 5 is matched by prioritizing \(\mathsf {Restart}\) over \(\mathsf {Fail}\) and \(\mathsf {Jump}\).

With the EDB E traced down in \(\mathcal{E}\), it remains to prove \((E,I) \models P\). With the stratification of P (Definition 1) this is rather straightforward. Range-restrictedness makes sure that only ground heads are derivable. The \(\mathsf {Ext}\) inference rule achieves on-the-fly splitting and only for those variable-free instances of rules whose body is satisfied, which are the only ones that count (details in [26, 27]). The requirements on EDB/IDB atoms in negative body literals (\(\le \) vs. <) in Definition 1 entail that utilizing body matchers in derivations is correct wrt. the rule semantics in Definition 2.

The timed setting requires a layered fixpoint iteration. Stratification makes sure that stepwisely incrementing in derivations the time bound by \(\mathsf {Jump}\) inferences is all that is needed to comply with the “unstepped” possible model semantics in Definitions 4 and 5. In particular, no ordinary rule can derive in its head a conclusion with a timestamp earlier than the latest timestamp in its body. This makes the derivability relation monotonic wrt. increasing time stamps (usual stratification by predicates is covered in [26]). Moreover, because the given derivation is exhausted, no fixpoint iteration can stop prematurely.

For completeness, assume \( mods _P(E_{\mathsf {init}}) \ne \emptyset \) and chose any \((E,I) \in mods _P(E_{\mathsf {init}})\) arbitrary. We have to show \((E,I) \in \mathcal{M}(D)\). The first step is to locate in D the sub-tableau with E at its root, by tracing \(E \in \mathcal{E}\) from \(E_{\mathsf {init}}\). The next step then is to argue for the completeness of the sub-tableau construction with that fixed E, giving \((E,I) \in T_n\). All that uses similar considerations as the soundness proof above.

One important detail is that \(\mathsf {Ext}\) is the highest-priority inference rule. This makes sure that no model candidate is terminated too early, so that all possible branching out takes place. As a consequence, all possible fail heads for the current time point will be derived. The requirement that there are no proper (non-constant) function symbols make sure that the layered fixpoint computation of derivations terminates and finds (EI).    \(\square \)

6 Implementation

It is not too difficult to translate the model computation calculus of Sect. 5 into a proof procedure. Tableaux can be represented in a direct way, as a set of paths (EIt). In terms of Fig. 2, the proof procedure can implement a one-branch-at-a-time approach for one sub-tableau at a time, for space efficiency, embedded in an adapted given-clause loop algorithm. Only the EDBs, not the full models, need to be remembered to implement the “Progress” condition for derivations (cf. Sect. 5).

A concrete implementation could based on, e.g., hyper resolution with splitting [5] or hyper tableaux [3] calculi. Our implementation however is implemented in an unusual way, by shallow embedding in Scala.Footnote 3

Scala [28] is a modern high-level programming language that combines object-oriented and functional programming styles. It has functions as first-class objects and supports user-definable pre-, post- and infix syntax. With these features, Scala is suitable as a host language for embedding domain-specific languages (DSLs). (See, e.g., [16] for a Scala DSL for runtime verification.) In our case, the logic program rules are nothing but partial functions, instantiating and evaluating a rule body reduces to partial function definedness, and deriving a rule head reduces to executing a partial function on a defined point. An advantage of the DSL approach is that is is easy to interface with external systems, e.g., databases, in particular if they have a Java interface.

Moreover, it is easy to make the full Scala language and its associated data structure libraries available for writing rules. (There is no theoretical problem doing that as long as all Scala expressions are ground and, hence, can be evaluated.) For example, the EDB in Example 3 has the Scala-set forming term , and the rules (7) - (9) in Fig. 1 test in their last lines membership in such sets by Scala expressions.

While EDBs are naturally written as Scala source code, logic program rules are usually written in a (much) more convenient syntax and translated into the required format by the Scala macro mechanism. See Listing 1.1 for an example.Footnote 4

figure b

In Listing 1.1, line 1 sets the concrete type for time to , the Scala integers. A realistic application could use a rich time class like . Lines 2, 3 and 4 define the EDB and IDB signature of the supply chain Example 2 by extension of the Scala classes and . The relation says that the object was loaded into the container at time . The relation says that was in container at time . The \( time \) argument must be named , but all other arguments and their types can be freely chosen. For simplicity we used strings, except for the relation, which has a set of strings for its parameter.

Line 6 defines a list with one rule that expresses the transitivity of the relation (rule (2) in Fig. 1). Line 5 is an annotation that tells the compiler to expand the subsequent definition by a macro named . Indeed, without the help of a macro the rule would not compile because of undefined variables in the rule. The macro expansion of the rule in line 6 is the Scala function in Listing 1.2.

figure t

The anonymous function in Listing 1.2 is passed in its formal parameter a set of atoms which will always be the “current interpretation” \((E \cup I)_\le t\) where \(p = (E,I,t)\) is the current path. The set is needed for evaluating negative body literals and is not relevant for this example. The function returns a partial function in the form of a case expression. The pattern in the case expression are the ordinary atom of the positive body literals. These are the ones that need to be matched to the atoms of \((E \cup I)_\le t\) for rule evaluation (see Note 1). The matching is done by applying the partial function to all tuples (of the proper arity) of elements from the current interpretation. If the application succeeds, i.e., if the case pattern match succeeds and the additional -condition is satisfied, the partial function body (to the right of ) is executed, which results in an instantiated head. If the head is disjunctive, all non-empty subsets are taken. This gives all split programs (cf. Definition 3) on the fly. The resulting sets are collected in one sweep for each rule and are candidates for extending the current path. \({{\,\mathrm{{\mathbf {\mathsf{{fail}}}}}\,}}\)-rules are processed in a similar way.

Notice that the pattern of a case expression needs to be linear, hence the renaming apart of pattern variables and the obvious equalities in the -condition. Notice also that substitutions are not explicitly represented, they are hidden in the Scala runtime system.

It remains to be explained how arithmetic atoms and negative body literals are macro-expanded. By way of example, consider, say, rule (7) in Fig. 1. Its arithmetic condition \(t < time \) is simply conjoined to the -condition of the rule’s case expression. An -expression like is a backdoor for adding arbitrary Boolean-valued Scala code to that condition (“” belongs to the Scala library and tests set membership). Recall that all variables in arithmetic atoms will always be ground instantiated by matching and hence can be evaluated. This must be be extended to Scala conditions. In the implementation, any such free variable would be detected as a compile time error.

The body literal is expanded into a partial function . It is structurally the same as the one in Listing 1.1 except that the binding of the variables and in the surrounding context need to respected, giving the stated equalities. Now, the if-condition of the case expression of the surrounding rule (i.e., rule 7) is conjoined with a Scala expression for testing that the partial function does not return , for no tuple of elements from the set explained above.

The implementation supports some more features not further discussed here: rules can be defined locally within case classes; literals – possibly negated atoms – can be used anywhere instead of atoms; a “strong fail” head operator terminates a model candidate without restarts, e.g., for classical negation: a and \(\lnot a\) together is unfixable; and Scala conditions can access the interpretation \((E \cup I)_{\le t}\) for, e.g., concise data aggregation.

7 Conclusions

This paper presented a novel calculus and implementation for situational awareness applications. The approach is meant to be practical in three ways: first, realistic situational awareness requires being able to reason with incomplete or erroneous data. Moreover, “anytime” reasoning is needed, meaning that a model can be derived, rejected or repaired at any current time. Our approach supports these needs with a (disjunctive) logic programming framework with timed predicates, stratified negation and a novel model revision operator. Second, thanks to implementation on top of Scala, it is trivial to attach arbitrary Scala code and Java libraries. (It would not be difficult to extend the calculus respectively.) For instance, reading in XML data and making them available as terms (Scala case classes) is easy. Third, we strived for a “cheap” model computation procedure that makes do without additional generate-and-test needs. As such, it is perhaps more adequately seen as study in pushing bottom-up first-order logic model computation technology rather than slimmed down answer-set programming.

As for future work, one interesting idea is to add probabilities to the picture, say, in the way ProbLog extends Prolog. This is obviously useful because, e.g., some explanations (models) or repairs (restarts) are more likely than others. Another idea is to view the model computation as runtime verification. This view suggests that (probabilistic) linear temporal logic could serve as an additional useful high-level specification language component.