figure a
figure b

1 Introduction

Bounded Model Checking (BMC) is a powerful technique for disproving safety properties of, e.g., software or hardware systems. However, as it uses breadth-first search to find counterexamples, the search space grows exponentially w.r.t. the bound, i.e., the limit on the length of potential counterexamples. Thus, finding deep counterexamples that require large bounds is challenging for BMC. On the other hand, acceleration techniques can compute a first-order formula that characterizes the transitive closure of the transition relation induced by a loop. Intuitively, such a formula corresponds to a “shortcut” that “compresses” many execution steps into a single one. In this paper, we consider relations defined by quantifier-free first-order formulas over some background theory like non-linear integer arithmetic and two disjoint vectors of variables \(\vec {x}\) and \(\vec {x}'\), called the pre- and post-variables. Such transition formulas can easily represent, e.g., transition systems (TSs), linear Constrained Horn Clauses (CHCs), and control-flow automata (CFAs).Footnote 1 Thus, they subsume many popular intermediate representations used for verification of systems specified in more expressive languages.

In contrast to, e.g., source code, transition formulas are completely unstructured. However, source code may be unstructured, too (e.g., due to gotos), i.e., one cannot rely on the input being well structured. So the fact that our approach is independent from the structure of the input makes it broadly applicable.

Example 1

Consider the transition formula \(\tau \mathrel {\mathop :}=\tau _{x<100} \vee \tau _{x=100}\) where

$$\begin{aligned} \tau _{x<100} & \mathrel {\mathop :}=x < 100 \wedge x' = x + 1 \wedge y' = y \quad \text {and}\\ \tau _{x=100} & \mathrel {\mathop :}=x = 100 \wedge x' = 0 \wedge y' = y + 1. \end{aligned}$$

It defines a relation \(\rightarrow _{\tau }\) on \(\mathbb {Z}\times \mathbb {Z}\) by relating the pre-variables x and y with the post-variables \(x'\) and \(y'\). So for all \(c_x,c_y, c'_x, c'_y \in \mathbb {Z}\),

figure c

we have \((c_x,c_y) \rightarrow _{\tau } (c'_x, c'_y)\) iff \([x/c_x,y/c_y,x'/c'_x,y'/c'_y]\) is a model of \(\tau \), i.e., iff there is a step from a state with \(x=c_x \wedge y=c_y\) to a state with \(x=c'_x \wedge y=c'_y\) in Listing 1. To prove that an error state satisfying \(\psi _{\textsf{err}} \mathrel {\mathop :}=~\hbox {y} \ge 100\) is reachable from an initial state which satisfies \(\psi _{\textsf{init}} \mathrel {\mathop :}=x \le 0 \wedge y \le 0\), BMC has to unroll \(\tau \) 10100 times.

Our new technique Accelerated BMC (ABMC) uses the following acceleration

figure d

of \(\tau _{x<100}\): As we have \((c_x,c_y) \rightarrow ^+_{\tau _{x<100}} (c'_x, c'_y)\) iff \({\tau _{\textsf{i}}^+}[x/c_x,y/c_y,x'/c'_x,y'/c'_y]\) is satisfiable, \({\tau _{\textsf{i}}^+}\) is a “shortcut” for many \(\rightarrow _{\tau _{x<100}}\)-steps.

To compute such a shortcut \({\tau _{\textsf{i}}^+}\) from the formula \(\tau _{x<100}\), we use existing acceleration techniques [18]. In the example above, n serves as loop counter. Then the literal \(x' = x + 1\) of \(\tau _{x<100}\) gives rise to the recurrence equations \(x^{(0)} = x\) and \(x^{(n)} = x^{(n-1)} + 1\), which yield the closed form \(x^{(n)} = x + n\), resulting in the literal \(x' = x + n\) of \({\tau _{\textsf{i}}^+}\). Thus, the literal \(x + n \le 100\) of \({\tau _{\textsf{i}}^+}\) is equivalent to \(x^{(n-1)} < 100\). As x is monotonically increasing (i.e., \(\tau _{x<100}\) implies \(x < x'\)), \(x^{(n-1)} < 100\) implies \(x^{(n-2)}< 100\), \(x^{(n-3)}< 100\), ..., \(x^{(0)} < 100\), i.e., the loop \(\tau _{x<100}\) can indeed be executed n times.

So \({\tau _{\textsf{i}}^+}\) can simulate arbitrarily many steps with \(\tau \) in a single step, as long as x does not exceed 100. Here, acceleration was applied to \(\tau _{x<100}\), i.e., the projection of \(\tau \) to the case \(x < 100\), which corresponds to the inner loop of Listing 1. We also call such projections transitions. Later, ABMC also accelerates the outer loop (consisting of \(\tau _{x=100}\), \(\tau _{x<100}\), and \({\tau _{\textsf{i}}^+}\)), resulting in

figure e

For technical reasons, our algorithm accelerates \([\tau _{x=100}, \tau _{x<100}, {\tau _{\textsf{i}}^+}]\) instead of just \([\tau _{x=100}, {\tau _{\textsf{i}}^+}]\), so that \({\tau _{\textsf{o}}^+}\) requires \(1 < x'\), i.e., it only covers cases where \(\tau _{x<100}\) is applied at least twice after \(\tau _{x=100}\). Details will be clarified in Sect. 3.2, see in particular Fig. 1. Using these shortcuts, ABMC can prove unsafety with bound 7.

While our main goal is to improve BMC’s capability to find deep counterexamples, the following straightforward observations can be used to block certain parts of the transition relation in ABMC:

  1. 1.

    After accelerating a sequence of transitions, the resulting accelerated transition should be preferred over that sequence of transitions.

  2. 2.

    If an accelerated transition has been used, then the corresponding sequence of transitions should not be used immediately afterwards.

Both observations exploit that an accelerated transition describes the transitive closure of the relation induced by the corresponding sequence of transitions. Due to its ability to block parts of the transition relation, ABMC is able to prove safety in cases where BMC would unroll the transition relation indefinitely.

Outline. After introducing preliminaries in Sect. 2, we show how to use acceleration in order to improve the BMC algorithm to ABMC in Sect. 3. To increase ABMCs capabilities for proving safety, Sect. 4 refines ABMC by integrating blocking clauses. In Sect. 5, we discuss related work, and in Sect. 6, we evaluate our implementation of ABMC in our tool LoAT.

2 Preliminaries

We assume familiarity with basics from many-sorted first-order logic [15]. Without loss of generality, we assume that all formulas are in negation normal form (NNF). \(\mathcal {V}\) is a countably infinite set of variables and \(\mathcal {A}\) is a first-order theory over a k-sorted signature \(\varSigma \) with carrier \(\mathcal {C}= (\mathcal {C}_{1},\ldots ,\mathcal {C}_{k})\). For each entity e, \(\mathcal {V}(e)\) is the set of variables that occur in e. \(\textsf{QF}(\varSigma )\) denotes the set of all quantifier-free first-order formulas over \(\varSigma \), and \(\textsf{QF}_\wedge (\varSigma )\) only contains conjunctions of \(\varSigma \)-literals. We let \(\top \) and \(\bot \) stand for “true” and “false”, respectively.

Given \(\psi \in \textsf{QF}(\varSigma )\) with \(\mathcal {V}(\psi ) = \vec {y}\), we say that \(\psi \) is \(\mathcal {A}\)-valid (written \(\models _\mathcal {A}\psi \)) if every model of \(\mathcal {A}\) satisfies the universal closure \(\forall \vec {y}.\ \psi \) of \(\psi \). Moreover, \(\sigma : \mathcal {V}(\psi ) \rightarrow \mathcal {C}\) is an \(\mathcal {A}\)-model of \(\psi \) (written \(\sigma \models _\mathcal {A}\psi \)) if \(\models _\mathcal {A}\sigma (\psi )\), where \(\sigma (\psi )\) results from \(\psi \) by instantiating all variables according to \(\sigma \). If \(\psi \) has an \(\mathcal {A}\)-model, then \(\psi \) is \(\mathcal {A}\)-satisfiable. We write \(\psi \models _\mathcal {A}\psi '\) for \(\models _\mathcal {A}(\psi \implies \psi ')\), and \(\psi \equiv _\mathcal {A}\psi '\) means \(\models _\mathcal {A}(\psi \iff \psi ')\). In the sequel, we omit the subscript \(\mathcal {A}\), and we just say “valid”, “model”, and “satisfiable”. We assume that \(\mathcal {A}\) is complete, i.e., we either have \(\models \psi \) or \(\models \lnot \psi \) for every closed formula over \(\varSigma \).

We write \(\vec {x}\) for sequences and \(x_i\) is the \(i^{th}\) element of \(\vec {x}\). We use “\(\mathrel {:\,\!:}\)” for concatenation of sequences, where we identify sequences of length 1 with their elements, so we may write, e.g., \(x\mathrel {:\,\!:} xs \) instead of \([x]\mathrel {:\,\!:} xs \).

Let \(d \in \mathbb {N}\) be fixed, and let \(\vec {x},\vec {x}' \in \mathcal {V}^d\) be disjoint vectors of pairwise different variables, called the pre- and post-variables. Each \(\tau \in \textsf{QF}(\varSigma )\) induces a transition relation \(\rightarrow _\tau \) on \(\mathcal {C}^d\) where \(\vec {s} \rightarrow _\tau \vec {t}\) iff \(\tau [\vec {x}/\vec {s},\vec {x}'/\vec {t}]\) is satisfiable. Here, \([\vec {x}/\vec {s},\vec {x}'/\vec {t}]\) denotes the substitution \(\theta \) with \(\theta (x_i) = s_i\) and \(\theta (x'_i) = t_i\) for all \(1 \le i \le d\). We refer to elements of \(\textsf{QF}(\varSigma )\) as transition formulas whenever we are interested in their induced transition relation. Moreover, we also refer to conjunctive transition formulas (i.e., elements of \(\textsf{QF}_\wedge (\varSigma )\)) as transitions. A safety problem \(\mathcal {T}\) is a triple \((\psi _{\textsf{init}}, \tau ,\psi _{\textsf{err}}) \in \textsf{QF}(\varSigma ) \times \textsf{QF}(\varSigma ) \times \textsf{QF}(\varSigma )\) where \(\mathcal {V}(\psi _{\textsf{init}}) \cup \mathcal {V}(\psi _{\textsf{err}}) \subseteq \vec {x}\). It is unsafe if there are \(\vec {s},\vec {t} \in \mathcal {C}^d\) such that \([\vec {x} / \vec {s}] \models \psi _\textsf{init}\), \(\vec {s} \rightarrow ^*_\tau \vec {t}\), and \([\vec {x} / \vec {t}] \models \psi _\textsf{err}\).

The composition of \(\tau \) and \(\tau '\) is \({\circledcirc }(\tau ,\tau ') \mathrel {\mathop :}=\tau [\vec {x}' / \vec {x}''] \wedge \tau '[\vec {x} / \vec {x}'']\) where \(\vec {x}'' \in \mathcal {V}^d\) is fresh. Here, we assume \(\mathcal {V}(\tau ) \cap \mathcal {V}(\tau ') \subseteq \vec {x} \cup \vec {x}'\) (which can be ensured by renaming other variables correspondingly). So \({\rightarrow _{{\circledcirc }(\tau ,\tau ')}} = {\rightarrow _\tau } \circ {\rightarrow _{\tau '}}\) (where \({\circ }\) denotes relational composition). For finite sequences of transition formulas we define \({\circledcirc }([]) \mathrel {\mathop :}=(\vec {x} = \vec {x}')\) (i.e., \(\rightarrow _{{\circledcirc }([])}\) is the identity relation) and \({\circledcirc }(\tau \mathrel {:\,\!:}\vec {\tau }) \mathrel {\mathop :}={\circledcirc }(\tau ,{\circledcirc }(\vec {\tau }))\). We abbreviate \({\rightarrow _{{\circledcirc }(\vec {\tau })}}\) by \({\rightarrow _{\vec {\tau }}}\).

figure f

Acceleration techniques compute the transitive closure of relations. In the following definition, we only consider conjunctive transition formulas, since many existing acceleration techniques do not support disjunctions [8], or approximate in the presence of disjunctions [18]. So the restriction to conjunctive formulas ensures that our approach works with arbitrary existing acceleration techniques.

Definition 2

(Acceleration). An acceleration technique is a function \(\textsf{accel}: \textsf{QF}_\wedge (\varSigma ) \rightarrow \textsf{QF}_\wedge (\varSigma ')\) such that \({\rightarrow _{\textsf{accel}(\tau )}} \subseteq {\rightarrow _{\tau }^+}\), where \(\varSigma '\) is the signature of a first-order theory \(\mathcal {A}'\).

We abbreviate \(\textsf{accel}({\circledcirc }(\vec {\tau }))\) by \(\textsf{accel}(\vec {\tau })\). So as we aim at finding counterexamples, we allow under-approximating acceleration techniques, i.e., we do not require \({\rightarrow _{\textsf{accel}(\tau )}} = {\rightarrow _{\tau }^+}\). Definition 2 allows \(\mathcal {A}' \ne \mathcal {A}\), as most theories are not “closed under acceleration”. For example, accelerating the following Presburger formula on the left may yield the non-linear formula on the right:

$$ x' = x + y \wedge y' = y \qquad \qquad n > 0 \wedge x' = x + n \cdot y \wedge y' = y. $$

3 From BMC to ABMC

In this section, we introduce accelerated bounded model checking. To this end, we first recapitulate bounded model checking in Sect. 3.1. Then we present ABMC in Sect. 3.2. To implement ABMC efficiently, heuristics to decide when to perform acceleration are needed. Thus, we present such a heuristic in Sect. 3.3.

3.1 Bounded Model Checking

Algorithm 1 shows how to implement BMC on top of an incremental SMT solver. In Line 1, the description of the initial states is added to the SMT problem. Here and in the following, for all \(i \in \mathbb {N}\) we define \(\mu _i(x) \mathrel {\mathop :}=x^{(i)}\) if \(x \in \mathcal {V}\setminus \vec {x}'\), and \(\mu _i(x') = x^{(i+1)}\) if \(x' \in \vec {x}'\). So in particular, we have \(\mu _i(\vec {x}) = \vec {x}^{(i)}\) and \(\mu _i(\vec {x}') = \vec {x}^{(i+1)}\), where we assume that \(\vec {x}^{(0)},\vec {x}^{(1)},\ldots \in \mathcal {V}^d\) are disjoint vectors of pairwise different variables. In the loop, we set a backtracking point with the “\(\textsf{push}()\)” command and add a suitably variable-renamed version of the description of the error states to the SMT problem in Line 3. Then we check for satisfiability to see if an error state is reachable with the current bound in Line 4. If this is not the case, the description of the error states is removed with the “\(\textsf{pop}()\)” command that deletes all formulas from the SMT problem that have been added since the last backtracking point. Then a variable-renamed version of the transition formula \(\tau \) is added to the SMT problem. If this results in an unsatisfiable problem in Line 5, then the whole search space has been exhausted, i.e., then \(\mathcal {T}\) is safe. Otherwise, we enter the next iteration.

Example 3

(BMC). For the first 100 iterations of Algorithm 1 on Example 1, all models found in Line 5 satisfy the \(1^{st}\) disjunct \(\mu _b(\tau _{x<100})\) of \(\mu _b(\tau )\). Then we may have \(x^{(100)} = 100\), so that the \(2^{nd}\) disjunct \(\mu _b(\tau _{x=100})\) of \(\mu _b(\tau )\) applies once and we get \(y^{(101)} = y^{(100)} + 1\). After another 100 iterations, the \(2^{nd}\) disjunct \(\mu _b(\tau _{x=100})\) may apply again, etc. After 100 applications of the \(2^{nd}\) disjunct (and thus a total of 10100 steps), there is a model with \(y^{(10100)} = 100\), so that unsafety is proven.

3.2 Accelerated Bounded Model Checking

To incorporate acceleration into BMC, we have to bridge the gap between (disjunctive) transition formulas and acceleration techniques, which require conjunctive transition formulas. To this end, we use syntactic implicants.

Definition 4

(Syntactic Implicant Projection [22]). Let \(\tau \in \textsf{QF}(\varSigma )\) be in NNF and assume \(\sigma \models \tau \). We define the syntactic implicants \(\textsf{sip}(\tau )\) of \(\tau \) as follows:

$$\begin{aligned} \textsf{sip}(\tau ,\sigma ) \mathrel {\mathop :}=\bigwedge \{\lambda \mid \lambda \text { is a literal of } \tau , \, \sigma \models \lambda \} \qquad \textsf{sip}(\tau ) \mathrel {\mathop :}=\{\textsf{sip}(\tau ,\sigma ) \mid \sigma \models \tau \} \end{aligned}$$

Since \(\tau \) is in NNF, \(\textsf{sip}(\tau ,\sigma )\) implies \(\tau \), and it is easy to see that \(\tau \equiv \bigvee \textsf{sip}(\tau )\). Whenever the call to the SMT solver in Line 5 of Algorithm 1 yields \(\textsf{sat}\), the resulting model gives rise to a sequence of syntactic implicants, called the trace. To define the trace formally, note that when we integrate acceleration into \(\textsf{BMC}\), we may not only add \(\tau \) to the SMT formula as in Line 4, but also learned transitions that result from acceleration. Thus, the following definition allows for changing the transition formula. In the sequel, \(\circ \) also denotes composition of substitutions, i.e., \(\theta ' \circ \theta \mathrel {\mathop :}=[x / \theta '(\theta (x)) \mid x \in {{\,\textrm{dom}\,}}(\theta ') \cup {{\,\textrm{dom}\,}}(\theta )]\).

Definition 5

(Trace). Let \([\tau _i]_{i=0}^{b-1}\) be a sequence of transition formulas and let \(\sigma \) be a model of \(\bigwedge _{i=0}^{b-1} \mu _i(\tau _i)\). Then the trace induced by \(\sigma \) is

$$ \textsf{trace}_b(\sigma ,[\tau _i]_{i=0}^{b-1}) \mathrel {\mathop :}=[\textsf{sip}(\tau _i, \sigma \circ \mu _i)]_{i=0}^{b-1}. $$

We write \(\textsf{trace}_b(\sigma )\) instead of \(\textsf{trace}_b(\sigma ,[\tau _i]_{i=0}^{b-1})\) if \([\tau _i]_{i=0}^{b-1}\) is clear from the context.

So each model \(\sigma \) of \(\bigwedge _{i=0}^{b-1} \mu _i(\tau _i)\) corresponds to a sequence of steps with the relations \(\rightarrow _{\tau _0}, \rightarrow _{\tau _1}, \ldots , \rightarrow _{\tau _{b-1}}\), and the trace induced by \(\sigma \) contains the syntactic implicants of the formulas \(\tau _i\) that were used in this sequence.

Example 6

(Trace). Reconsider Example 3. After two iterations of the loop of Algorithm 1, the SMT problem consists of the following formulas:

figure g

With \(\sigma = [x^{(i)}/i,y^{(i)}/0 \mid 0 \le i \le 2]\), we get \(\textsf{trace}_2(\sigma ) = [\tau _{x<100},\tau _{x<100}]\), as:

$$\begin{aligned} \textsf{sip}(\tau , \sigma \circ \mu _0) & = \textsf{sip}(\tau , [x/0,y/0,x'/1,y'/0]) = \tau _{x<100} \\ \textsf{sip}(\tau , \sigma \circ \mu _1) & = \textsf{sip}(\tau , [x/1,y/0,x'/2,y'/0]) = \tau _{x<100} \end{aligned}$$

To detect situations where applying acceleration techniques pays off, we need to distinguish traces that contain loops from non-looping ones. Since transition formulas are unstructured, the usual techniques for detecting loops (based on, e.g., program syntax or control flow graphs) do not apply in our setting. Instead, we rely on the dependency graph of the transition formula.

figure h

Definition 7

(Dependency Graph). Let \(\tau \) be a transition formula. Its dependency graph \(\mathcal{D}\mathcal{G}= (V,E)\) is a directed graph whose vertices \(V \mathrel {\mathop :}=\textsf{sip}(\tau )\) are \(\tau \)’s syntactic implicants, and \(\tau _1 \rightarrow \tau _2 \in E\) if \({\circledcirc }(\tau _1, \tau _2)\) is satisfiable. We say that \(\vec {\tau } \in \textsf{sip}(\tau )^c\) is \(\mathcal{D}\mathcal{G}\)-cyclic if \(c>0\) and \((\tau _1 \rightarrow \tau _{2}),\ldots ,(\tau _{c-1} \rightarrow \tau _{c}),(\tau _c \rightarrow \tau _1) \in E\).

figure i

So intuitively, the syntactic implicants correspond to the different cases of \(\rightarrow _\tau \), and \(\tau \)’s dependency graph corresponds to the control flow graph of \(\rightarrow _\tau \). The dependency graph for Example 1 is on the side.

However, as the size of \(\textsf{sip}(\tau )\) is worst-case exponential in the number of disjunctions in \(\tau \), we do not compute \(\tau \)’s dependency graph eagerly. Instead, ABMC maintains an under-approximation, i.e., a subgraph \(\mathcal {G}\) of the dependency graph, which is extended whenever two transitions that are not yet connected by an edge occur consecutively on the trace. As soon as a \(\mathcal {G}\)-cyclic suffix \(\vec {\tau }^\circlearrowleft \) is detected on the trace, we may accelerate it. Therefore, the trace may also contain the learned transition \(\textsf{accel}(\vec {\tau }^\circlearrowleft )\) in subsequent iterations. Hence, to detect cyclic suffixes that contain learned transitions, they have to be represented in \(\mathcal {G}\) as well. Thus, \(\mathcal {G}\) is in fact a subgraph of the dependency graph of \(\tau \vee \bigvee \mathcal {L}\), where \(\mathcal {L}\) is the set of all transitions that have been learned so far.

This gives rise to the ABMC algorithm, which is shown in Algorithm 2. Here, we just write “cyclic” instead of (VE)-cyclic. The difference to Algorithm 1 can be seen in Lines 6 and 7. In Line 6, the trace is constructed from the current model. Then, the approximation of the dependency graph is refined such that it contains vertices for all elements of the trace, and edges for consecutive elements of the trace. In Line 7, a cyclic suffix of the trace may get accelerated, provided that the call to \(\mathsf {should\_accel}\) (which will be discussed in detail in Sect. 3.3) returns \(\top \). In this way, in the next iteration the SMT solver can choose a model that satisfies \(\textsf{accel}(\vec {\pi }^\circlearrowleft )\) and thus simulates several instead of just one \(\rightarrow _\tau \)-step. Note, however, that we do not update \(\tau \) with \(\tau \vee \textsf{accel}(\vec {\pi }^\circlearrowleft )\). So in every iteration, at most one learned transition is added to the SMT problem. In this way, we avoid blowing up \(\tau \) unnecessarily. Note that we only accelerate “real” cycles \(\vec {\pi }^\circlearrowleft \) where \({\circledcirc }(\vec {\pi }^\circlearrowleft )\) is satisfiable, since \(\vec {\pi }^\circlearrowleft \) is a suffix of the trace, whose satisfiability is witnessed by \(\sigma \).

As we rely on syntactic implicants and dependency graphs to detect cycles, \(\textsf{ABMC}\) is decoupled from the specific encoding of the input. So for example, transition formulas may be represented in CNF, DNF, or any other structure.

Figure 1 shows a run of Algorithm 2 on Example 1, where the formulas that are added to the SMT problem are highlighted in , and \(x^{(i)} \mapsto c\) abbreviates \(\sigma (x^{(i)}) = c\). For simplicity, we assume that \(\mathsf {should\_accel}\) always returns \(\top \), and the model \(\sigma \) is only extended in each step, i.e., \(\sigma (x^{(i)})\) and \(\sigma (y^{(i)})\) remain unchanged for all \(0 \le i < b\). In general, the SMT solver can choose different values for \(\sigma (x^{(i)})\) and \(\sigma (y^{(i)})\) in every iteration. On the right, we show the current bound b, and the formulas that give rise to the formulas on the left when renaming their variables suitably with \(\mu _b\). Initially, the approximation \(\mathcal {G}= (V,E)\) of the dependency graph is empty. When \(b=2\), the trace is \([\tau _{x < 100},\tau _{x < 100}]\), and the corresponding edge is added to \(\mathcal {G}\). Thus, the trace has the cyclic suffix \(\tau _{x < 100}\) and we accelerate it, resulting in \({\tau _{\textsf{i}}^+}\), which is added to the SMT problem. Then we obtain the trace \([\tau _{x < 100},\tau _{x < 100},{\tau _{\textsf{i}}^+}]\), and the edge \(\tau _{x < 100} \rightarrow {\tau _{\textsf{i}}^+}\) is added to \(\mathcal {G}\). Note that Algorithm 2 does not enforce the use of \({\tau _{\textsf{i}}^+}\), so \(\tau \) might still be unrolled instead, depending on the models found by the SMT solver. We will address this issue in Sect. 4.

Fig. 1.
figure 1

Running ABMC on Example 1

Next, \(\tau _{x = 100}\) already applies with \(b=4\) (whereas it only applied with \(b=100\) in Example 3). So the trace is \([\tau _{x < 100},\tau _{x < 100},{\tau _{\textsf{i}}^+},\tau _{x = 100}]\), and the edge \({\tau _{\textsf{i}}^+} \rightarrow \tau _{x = 100}\) is added to \(\mathcal {G}\). Then we obtain the trace \([\tau _{x < 100},\tau _{x < 100},{\tau _{\textsf{i}}^+},\tau _{x = 100},\tau _{x < 100}]\), and add \(\tau _{x = 100} \rightarrow \tau _{x < 100}\) to \(\mathcal {G}\). Since the suffix \(\tau _{x < 100}\) is again cyclic, we accelerate it and add \({\tau _{\textsf{i}}^+}\) to the SMT problem. After one more step, the trace \([\tau _{x < 100},\tau _{x < 100},{\tau _{\textsf{i}}^+},\tau _{x = 100},\tau _{x < 100},{\tau _{\textsf{i}}^+}]\) has the cyclic suffix \([\tau _{x = 100},\tau _{x < 100},{\tau _{\textsf{i}}^+}]\). Accelerating it yields \({\tau _{\textsf{o}}^+}\), which is added to the SMT problem. Afterwards, unsafety can be proven with \(b=7\).

Since using acceleration is just a heuristic to speed up BMC, all basic properties of BMC immediately carry over to ABMC.

Theorem 8

(Properties of ABMC). ABMC is

  • Sound: If \(\textsf{ABMC}(\mathcal {T})\) returns \(\mathsf {(un)safe}\), then \(\mathcal {T}\) is (un)safe.

  • Refutationally Complete: If \(\mathcal {T}\) is unsafe, then \(\textsf{ABMC}(\mathcal {T})\) returns \(\textsf{unsafe}\).

  • Non-Terminating: If \(\mathcal {T}\) is safe, then \(\textsf{ABMC}(\mathcal {T})\) may not terminate.

3.3 Fine Tuning Acceleration

We now discuss \(\mathsf {should\_accel}\), our heuristic for applying acceleration. To explain the intuition of our heuristic, we assume that acceleration does not approximate and thus \({\rightarrow _{\textsf{accel}(\vec {\tau })}} = {\rightarrow ^+_{\vec {\tau }}}\), but in our implementation, we also use it if \({\rightarrow _{\textsf{accel}(\vec {\tau })}} \subset {\rightarrow ^+_{\vec {\tau }}}\). This is uncritical for correctness, as using acceleration in Algorithm 2 is always sound.

First, acceleration should be applied to cyclic suffixes consisting of a single original (i.e., non-learned) transition. However, applying acceleration to a single learned transition is pointless, as

$$ {\rightarrow _{\textsf{accel}(\textsf{accel}(\tau ))}} = {\rightarrow _{\textsf{accel}(\tau )}^+} = {(\rightarrow ^+_{\tau })^+} = {\rightarrow ^+_{\tau }} = {\rightarrow _{\textsf{accel}(\tau )}}. $$

Requirement 1

\(\mathsf {should\_accel}([\pi ]) = \top \) iff \(\pi \in \textsf{sip}(\tau )\).

Next, for every cyclic sequence \(\vec {\pi }\), we have

$$\begin{aligned} {\rightarrow _{\textsf{accel}(\vec {\pi }:\,\!:\textsf{accel}(\vec {\pi }))}} = {\rightarrow ^+_{\vec {\pi }:\,\!:\textsf{accel}(\vec {\pi })}} = ({\rightarrow _{\vec {\pi }} \circ \rightarrow _{\vec {\pi }}^+})^+ = {\rightarrow _{\vec {\pi }} \circ \rightarrow _{\vec {\pi }}^+} = {\rightarrow _{\vec {\pi }:\,\!:\textsf{accel}(\vec {\pi })}}, \end{aligned}$$

and thus accelerating \(\vec {\pi }:\,\!:\textsf{accel}(\vec {\pi })\) is pointless, too. More generally, we want to prevent acceleration of sequences \(\vec {\pi }_2:\,\!:\textsf{accel}(\vec {\pi }):\,\!:\vec {\pi }_1\) where \(\vec {\pi } = \vec {\pi }_1:\,\!:\vec {\pi }_2\) as

$$ {\rightarrow _{\vec {\pi }_2:\,\!:\textsf{accel}(\vec {\pi }):\,\!:\vec {\pi }_1}^2} = {\rightarrow _{\vec {\pi }_2:\,\!:\textsf{accel}(\vec {\pi }):\,\!:\vec {\pi }:\,\!:\textsf{accel}(\vec {\pi }):\,\!:\vec {\pi }_1}} \subseteq {\rightarrow _{\vec {\pi }_2:\,\!:\textsf{accel}(\vec {\pi }):\,\!:\vec {\pi }_1}} $$

and thus \({\rightarrow _{\textsf{accel}(\vec {\pi }_2:\,\!:\textsf{accel}(\vec {\pi }):\,\!:\vec {\pi }_1)}} = {\rightarrow _{\vec {\pi }_2:\,\!:\textsf{accel}(\vec {\pi }):\,\!:\vec {\pi }_1}^+} = {\rightarrow _{\vec {\pi }_2:\,\!:\textsf{accel}(\vec {\pi }):\,\!:\vec {\pi }_1}}\). So in general, the cyclic suffix of such a trace consists of a cycle \(\vec {\pi }\) and \(\textsf{accel}(\vec {\pi })\), but it does not necessarily start with either of them. To take this into account, we rely on the notion of conjugates.

Definition 9

(Conjugate). We say that two vectors \(\vec {v},\vec {w}\) are conjugates (denoted \(\vec {v} \equiv _\circ \vec {w}\)) if \(\vec {v}=\vec {v}_1 \mathrel {:\,\!:}\vec {v}_2\) and \(\vec {w}=\vec {v}_2 \mathrel {:\,\!:}\vec {v}_1\).

So a conjugate of a cycle corresponds to the same cycle with another entry point.

Requirement 2

\(\mathsf {should\_accel}(\vec {\pi }') = \bot \) if \(\vec {\pi }' \equiv _\circ \vec {\pi } \mathrel {:\,\!:}\textsf{accel}(\vec {\pi })\) for some \(\vec {\pi }\).

In general, however, we also want to accelerate cyclic suffixes that contain learned transitions to deal with nested loops, as in the last acceleration step of Fig. 1.

Requirement 3

\(\mathsf {should\_accel}(\vec {\pi }') = \top \) if \(\vec {\pi }' \not \equiv _\circ \vec {\pi }:\,\!:\textsf{accel}(\vec {\pi })\) for all \(\vec {\pi }\).

Requirements 1 to 3 give rise to a complete specification for \(\mathsf {should\_accel}\): If the cyclic suffix is a singleton, the decision is made based on Requirement 1, and otherwise the decision is made based on Requirements 2 and 3. However, this specification misses one important case: Recall that the trace was \([\tau _{x<100},\tau _{x<100}]\) before acceleration was applied for the first time in Fig. 1. While both \([\tau _{x<100}]\) and \([\tau _{x<100},\tau _{x<100}]\) are cyclic, the latter should not be accelerated, since \(\textsf{accel}([\tau _{x<100},\tau _{x<100}])\) is a special case of \({\tau _{\textsf{i}}^+}\) that only represents an even number of steps with \(\tau _{x<100}\). Here, the problem is that the cyclic suffix contains a square, i.e., two adjacent repetitions of the same non-empty sub-sequence.

Requirement 4

\(\mathsf {should\_accel}(\vec {\pi }) = \bot \) if \(\vec {\pi }\) contains a square.

Thus, \(\mathsf {should\_accel}(\vec {\pi }')\) yields \(\top \) iff the following holds:

$$\begin{aligned} (|\vec {\pi }'| = 1 \wedge \vec {\pi }' \in \textsf{sip}(\tau )) \vee ( |\vec {\pi }'| > 1 \wedge \vec {\pi }' \text { is square-free} \wedge \forall \vec {\pi }.\ (\vec {\pi }' \not \equiv _\circ \vec {\pi }\mathrel {:\,\!:}\textsf{accel}(\vec {\pi }))) \end{aligned}$$

All properties that are required to implement \(\mathsf {should\_accel}\) can easily be checked automatically. To check \(\vec {\pi }' \not \equiv _\circ \vec {\pi } \mathrel {:\,\!:}\textsf{accel}(\vec {\pi })\), our implementation maintains a map from learned transitions to the corresponding cycles that have been accelerated.

However, to implement Algorithm 2, there is one more missing piece: As the choice of the cyclic suffix in Line 7 is non-deterministic, a heuristic for choosing it is required. In our implementation, we choose the shortest cyclic suffix such that \(\mathsf {should\_accel}\) returns \(\top \). The reason is that, as observed in [22], accelerating short cyclic suffixes before longer ones allows for learning more general transitions.

4 Guiding ABMC with Blocking Clauses

As mentioned in Sect. 3.2, Algorithm 2 does not enforce the use of learned transitions. Thus, depending on the models found by the SMT solver, ABMC may behave just like BMC. We now improve ABMC by integrating blocking clauses that prevent it from unrolling loops instead of using learned transitions. Here, we again assume \({\rightarrow _{\textsf{accel}(\vec {\tau })}} = {\rightarrow ^+_{\vec {\tau }}}\), i.e., that acceleration does not approximate. Otherwise, blocking clauses are only sound for proving unsafety, but not for proving safety.

Blocking clauses exploit the following straightforward observation: If the learned transition \(\tau _\ell = \textsf{accel}(\vec {\pi }^\circlearrowleft )\) has been added to the SMT problem with bound b and an error state can be reached via a trace with prefix

$$ \vec {\pi } = [\tau _0,\ldots ,\tau _{b-1}]:\,\!:\vec {\pi }^\circlearrowleft \qquad \text {or} \qquad \vec {\pi }' = [\tau _0,\ldots ,\tau _{b-1},\tau _\ell ]:\,\!:\vec {\pi }^\circlearrowleft , $$

then an error state can also be reached via a trace with the prefix \([\tau _0,\ldots ,\tau _{b-1},\tau _\ell ]\), which is not continued with \(\vec {\pi }^\circlearrowleft \). Thus, we may remove traces of the form \(\vec {\pi }\) and \(\vec {\pi }'\) from the search space by modifying the SMT problem accordingly.

To do so, we assign a unique identifier to each learned transition, and we introduce a fresh integer-valued variable \(\ell \) which is set to the corresponding identifier whenever a learned transition is used, and to 0, otherwise.

Example 10

(Blocking Clauses). Reconsider Fig. 1 and assume that we modify \(\tau \) by conjoining \(\ell = 0\), and \({\tau _{\textsf{i}}^+}\) by conjoining \(\ell = 1\). Thus, we now have

$$\begin{aligned} \tau _{x<100} & \equiv x < 100 \wedge x' = x + 1 \wedge y' = y \wedge \ell = 0 & \text {and} \\ \tau _{\textsf{i}}^+ & \equiv n > 0 \wedge x + n \le 100 \wedge x' = x + n \wedge y' = y \wedge \ell = 1. \end{aligned}$$

When \(b=2\), the trace is \([\tau _{x<100},\tau _{x<100}]\), and in the next iteration, it may be extended to either \(\vec {\pi } = [\tau _{x<100},\tau _{x<100},\tau _{x<100}]\) or \(\vec {\tau } = [\tau _{x<100},\tau _{x<100},\tau _{\textsf{i}}^+]\). However, as \({\rightarrow _{\tau _{\textsf{i}}^+}} = {\rightarrow ^+_{\tau _{x < 100}}}\), we have \({\rightarrow _{\vec {\pi }}} \subseteq {\rightarrow _{\vec {\tau }}}\), so the entire search space can be covered without considering the trace \(\vec {\pi }\). Thus, we add the blocking clause

figure k

to the SMT problem to prevent ABMC from finding a model that gives rise to the trace \(\vec {\pi }\). Note that we have \(\mu _2(\tau _{\textsf{i}}^+) \models \beta _1\), as \(\tau _{x<100} \models \ell = 0\) and \(\tau _{\textsf{i}}^+ \models \ell \ne 0\). Thus, \(\beta _1\) blocks \(\tau _{x < 100}\) for the third step, but \(\tau _{\textsf{i}}^+\) can still be used without restrictions. Therefore, adding \(\beta _1\) to the SMT problem does not prevent us from covering the entire search space.

Similarly, we have \({\rightarrow _{\vec {\pi }'}} \subseteq {\rightarrow _{\vec {\tau }}}\) for \(\vec {\pi }' = [\tau _{x<100},\tau _{x<100},\tau _{\textsf{i}}^+,\tau _{x<100}]\). Thus, we also add the following blocking clause to the SMT problem:

figure l
figure m

ABMC with blocking clauses can be seen in Algorithm 3. The counter \(\textsf{id}\) is used to obtain unique identifiers for learned transitions. Thus, it is initialized with 0 (Line 1) and incremented whenever a new transition is learned (Line 11). Moreover, as explained above, \(\ell = 0\) is conjoined to \(\tau \) (Line 1), and \(\ell = \textsf{id}\) is conjoined to each learned transition (Line 11).

In Lines 13 and 14, the blocking clauses \(\beta _1\) and \(\beta _2\) which correspond to the superfluous traces \(\vec {\pi }\) and \(\vec {\pi }'\) above are created, and they are added to the SMT problem in Line 15. Here, \(\pi _i^\circlearrowleft \) denotes the \(i^{th}\) transition in the sequence \(\vec {\pi }^\circlearrowleft \).

Importantly, Algorithm 3 caches (Line 12) and reuses (Line 9) learned transitions. In this way, the learned transitions that are conjoined to the SMT problem have the same \(\textsf{id}\) if they stem from the same cycle, and thus the blocking clauses \(\beta _1\) and \(\beta _2\) can also block sequences \(\vec {\pi }^\circlearrowleft \) that contain learned transitions, as shown in the following example.

Example 11

(Caching). Let \(\tau \) have the dependency graph given below. As Alg. 3

figure n

conjoins \(\ell = 0\) to \(\tau \), assume \(\tau _i \models \ell = 0\) for all \(i \in \{1,2,3\}\). Moreover, assume that accelerating \(\tau _2\) yields \(\tau _2^+\) with \(\tau _2^+ \models \ell = 1\). If we obtain the trace \([\tau _1,\tau _2^+,\tau _3]\), it can be accelerated. Thus, Alg. 3 would add

$$ \beta _1 \equiv \lnot \left( \mu _3(\tau _1) \wedge \mu _4(\tau ^+_2) \wedge \mu _5(\tau _3)\right) $$

to the SMT problem. If the next step yields the trace \([\tau _1,\tau _2^+,\tau _3,\tau _2]\), then \(\tau _2\) is accelerated again. Without caching, acceleration may yield a new transition \(\tau _{2'}^+\) with \(\tau _{2'}^+ \models \ell = 2\). As the SMT solver may choose a different model in every iteration, the trace may also change in every iteration. So after two more steps, we could get the trace \([\tau _1,\tau _2^+,\tau _3,\tau _1,\tau _{2'}^+,\tau _3]\). At this point, the “outer” loop consisting of \(\tau _1\), arbitrarily many repetitions of \(\tau _2\), and \(\tau _3\), has been unrolled a second time, which should have been prevented by \(\beta _1\). The reason is that \(\tau _{2}^+ \models \ell = 1\), whereas \(\tau _{2'}^+ \models \ell = 2\), and thus \(\tau _{2'}^+ \models \lnot \tau ^+_2\). With caching, we again obtain \(\tau _2^+\) when \(\tau _2\) is accelerated for the second time, such that this problem is avoided.

Remarkably, blocking clauses allow us to prove safety in cases where BMC fails.

Example 12

(Proving Safety with Blocking Clauses). Consider the safety problem \((x \le 0,\tau ,x>100)\) with \(\tau \equiv x < 100 \wedge x' = x + 1\). Algorithm 1 cannot prove its safety, as \(\tau \) can be unrolled arbitrarily often (by choosing smaller and smaller initial values for x). With Algorithm 3, we obtain the following SMT problem with \(b=3\).

figure o

From the last formula and \(\beta _2\), we get \(\ell ^{(2)} \ne 1\), but the formula labeled with (\(\tau \vee \textsf{accel}(\tau )\)) and \(\beta _1\) imply \(\mu _2(\ell = 1) \equiv \ell ^{(2)} = 1\), resulting in a contradiction. Thus, due to the blocking clauses, \(\textsf{ABMC}_\textsf{b}\) can prove safety with the bound \(b=3\).

Like \(\textsf{ABMC}\), \(\textsf{ABMC}_\textsf{b}\) preserves \(\textsf{BMC}\)’s main properties (see [23] for a proof).

Theorem 13

\(\textsf{ABMC}_\textsf{b}\) is sound and refutationally complete, but non-terminating.

5 Related Work

There is a large body of literature on bounded model checking that is concerned with encoding temporal logic specifications into propositional logic, see [5, 6] as starting points. This line of work is clearly orthogonal to ours.

Moreover, numerous techniques focus on proving safety or satisfiability of transition systems or CHCs, respectively (e.g., [13, 17, 25, 27, 29, 36]). A comprehensive overview is beyond the scope of this paper. Instead, we focus on techniques that, like ABMC, aim to prove unsafety by finding long counterexamples.

The most closely related approach is Acceleration Driven Clause Learning [21, 22], a calculus that uses depth-first search and acceleration to find counterexamples. So one major difference between ABMC and ADCL is that ABMC performs breadth-first search, whereas ADCL performs depth-first search. Thus, ADCL requires a mechanism for backtracking to avoid getting stuck. To this end, it relies on a notion of redundancy, which is difficult to automate. Thus, in practice, approximations are used [22, Sect. 4]. However, even with a complete redundancy check, ADCL might get stuck in a safe part of the search space [22, Thm. 18]. ABMC does not suffer from such deficits.

Like ADCL, ABMC also tries to avoid redundant work (see Sects. 3.3 and 4). However, doing so is crucial for ADCL due to its depth-first strategy, whereas it is a mere optimization for ABMC.

On the other hand, ADCL applies acceleration in a very systematic way, whereas ABMC decides whether to apply acceleration or not based on the model that is found by the underlying SMT solver. Therefore, ADCL is advantageous for examples with deeply nested loops, where ABMC may require many steps until the SMT solver yields models that allow for accelerating the nested loops one after the other. Furthermore, ADCL has successfully been adapted for proving non-termination [21], and it is unclear whether a corresponding adaption of ABMC would be competitive. Thus, both techniques are orthogonal. See Sect. 6 for an experimental comparison of ADCL with ABMC.

Other acceleration-based approaches [4, 9, 19] can be seen as generalizations of the classical state elimination method for finite automata: Instead of transforming finite automata to regular expressions, they transform transition systems to formulas that represent the runs of the transition system. During this transformation, acceleration is the counterpart to the Kleene star in the state elimination method. Clearly, these approaches differ fundamentally from ours.

In [30], under-approximating acceleration techniques are used to enrich the control-flow graph of C programs. Then an external model checker is used to find counterexamples. In contrast, ABMC tightly integrates acceleration into BMC, and thus enables an interplay of both techniques: Acceleration changes the state of the bounded model checker by adding learned transitions to the SMT problem. Vice versa, the state of the bounded model checker triggers acceleration. Doing so is impossible if the bounded model checker is used as an external black box.

In [31], the approach from [30] is extended by a program transformation that, like our blocking clauses, rules out superfluous traces. For structured programs, program transformations are quite natural. However, as we analyze unstructured transition formulas, such a transformation would be very expensive in our setting. More precisely, [31] represents programs as CFAs. To transform them, the edges of the CFA are inspected. In our setting, the syntactic implicants correspond to these edges. An important goal of ABMC is to avoid computing them explicitly. Hence, it is unclear how to apply the approach from [31] in our setting.

Another related approach is described in [26], where acceleration is integrated into a CEGAR loop in two ways: (1) as preprocessing and (2) to generalize interpolants. In contrast to (1), we use acceleration “on the fly”. In contrast to (2), we do not use abstractions, so our learned transitions can directly be used in counterexamples. Moreover, [26] only applies acceleration to conjunctive transition formulas, whereas we accelerate conjunctive variants of arbitrary transition formulas. So in our approach, acceleration techniques are applicable more often, which is particularly useful for finding long counterexamples.

Finally, transition power abstraction (TPA) [7] computes a sequence of over-approximations for transition systems where the \(n^{th}\) element captures \(2^n\) instead of just n steps of the transition relation. So like ABMC, TPA can help to find long refutations quickly, but in contrast to ABMC, TPA relies on over-approximations.

6 Experiments and Conclusion

We presented ABMC, which integrates acceleration techniques into bounded model checking. By enabling BMC to find deep counterexamples, it targets a major limitation of BMC. However, whether ABMC makes use of transitions that result from acceleration depends on the models found by the underlying SMT solver. Hence, we introduced blocking clauses to enforce the use of accelerated transitions, which also enable ABMC to prove safety in cases where BMC fails.

We implemented ABMC in our tool LoAT [20]. It uses the SMT solvers Z3 [33] and Yices [14]. Currently, our implementation is restricted to integer arithmetic. It uses the acceleration technique from [18] which, in our experience, is precise in most cases where the values of the variables after executing the loop can be expressed by polynomials of degree \(\le 2\) (i.e., here we have \({\rightarrow _{\textsf{accel}(\tau )}} = {\rightarrow _{\tau }^+}\)). If acceleration yields a non-polynomial formula, then this formula is discarded by our implementation, since Z3 and Yices only support polynomials. We evaluate our approach on the examples from the category LIA-Lin (linear CHCs with linear integer arithmetic)Footnote 2 from the CHC competition ’23 [11], which contain problems from numerous applications like verification of C, Rust, Java, and higher-order programs, and regression verification of LLVM programs, see [12] for details. By using CHCs as input format, our approach can be used by any CHC-based tool like Korn [16] and SeaHorn [24] for C and programs, JayHorn for Java programs [28], HornDroid for Android [10], RustHorn for Rust programs [32], and SmartACE [35] and SolCMC [3] for Solidity.

We compared several configurations of LoAT with the techniques of other leading CHC solvers. More precisely, we evaluated the following configurations:

  • LoAT We used LoAT’s implementations of Algorithm 1 (LoAT \(\textsf{BMC}\)), Algorithm 2 (LoAT \(\textsf{ABMC}\)), Algorithm 3 (LoAT \(\textsf{ABMC}_\textsf{b}\)), and ADCL (LoAT ADCL).

  • Z3 [33] We used Z3 4.13.0, where we evaluated its implementations of the Spacer algorithm (Spacer [29]) and BMC (Z3 BMC).

  • Golem [7] We used Golem 0.5.0, where we evaluated its implementations of transition power abstraction (Golem TPA [7]) and BMC (Golem BMC).

  • Eldarica [27] We used Eldarica 2.1.0. We tested all five configurations that are used in parallel in its portfolio mode (-portfolio), and included the two that found the most counterexamples: CEGAR with acceleration as preprocessing (Eldarica CEGAR, eld -splitClauses:1 -abstract:off -stac) and symbolic execution (Eldarica SYM, eld -splitClauses:1 -sym).

Note that all configurations except Spacer and Eldarica CEGAR are specifically designed for finding counterexamples. We did not include further techniques for proving safety in our evaluation, as our focus is on disproving safety. We ran our experiments on StarExec [34] with a wallclock timeout of 300s, a cpu timeout of 1200s, and a memory limit of 128 GB per example.

figure q
figure r

The results can be seen in the table above. The columns with ! show the number of unique proofs, i.e., the number of examples that could only be solved by the corresponding configuration. Such a comparison only makes sense if just one implementation of each algorithm is considered. For instance, LoAT’s, Z3’s, and Golem’s implementations of the BMC algorithm work well on the same class of examples, so that none of them finds unique proofs if all of them are taken into account. Thus, for ! we disregarded LoAT \(\textsf{ABMC}\), Z3 BMC, and Golem BMC.

The table shows that our implementation of ABMC is very powerful for proving unsafety. In particular, it shows a significant improvement over LoAT BMC, which is implemented very similarly, but does not make use of acceleration.

Note that all unsafe instances that can be solved by ABMC can also be solved by other configurations. This is not surprising, as LoAT ADCL is also based on acceleration techniques. Hence, ABMC combines the strengths of ADCL and BMC, and conversely, unsafe examples that can be solved with ABMC can usually also be solved by one of these techniques. So for unsafe instances, the main contribution of ABMC is to have one technique that performs well both on instances with shallow counterexamples (which can be solved by BMC) as well as instances with deep counterexamples only (which can often be solved by ADCL).

On the instance that can only be solved by ADCL, our (A)BMC implementation spends most of the time with applying substitutions, which clearly shows potential for further optimizations. Due to ADCL’s depth-first strategy, it produces smaller formulas, so that applying substitutions is cheaper.

Regarding safe examples, the table shows that our implementation of ABMC is not competitive with state-of-the-art techniques.Footnote 3 However, it finds several unique proofs. This is remarkable, as LoAT is not at all fine-tuned for proving safety. For example, we expect that LoAT’s results on safe instances can easily be improved by integrating over-approximating acceleration techniques. While such a variant of ABMC could not prove unsafety, it would presumably be much more powerful for proving safety. We leave that to future work.

figure s

The plot on the previous page shows how many unsafety proofs were found within 300 s, where we only include the six best configurations for readability. It shows that ABMC is highly competitive on unsafe instances, not only in terms of solved examples, but also in terms of runtime. The plot on the right compares the length of the counterexamples found by LoAT \(\textsf{ABMC}_\textsf{b}\) and \(\textsf{BMC}\) to show the impact of acceleration. Here, only examples where both techniques disprove safety are considered, and the counterexamples found by \(\textsf{ABMC}_\textsf{b}\) may contain accelerated transitions. There are no points below the diagonal, i.e., the counterexamples found by \(\textsf{ABMC}_\textsf{b}\) are at most as long as those found by \(\textsf{BMC}\). The points above the diagonal indicate that the counterexamples found by \(\textsf{ABMC}_\textsf{b}\) are sometimes shorter by orders of magnitude (note that the axes are log-scaled).

Our results also show that blocking clauses have no significant impact on ABMC’s performance on unsafe instances, neither regarding the number of solved examples, nor regarding the runtime. In fact, \(\textsf{ABMC}_\textsf{b}\) solved one instance less than \(\textsf{ABMC}\) (which can, however, also be solved by \(\textsf{ABMC}_\textsf{b}\) with a larger timeout). On the other hand, blocking clauses are clearly useful for proving safety, where they even allow LoAT to find several unique proofs.

In future work, we plan to support other theories like reals, bitvectors, and arrays, and we will investigate an extension to non-linear CHCs. Our implementation is open-source and available on Github. For the sources, a pre-compiled binary, and more information on our evaluation, we refer to [2].