1 Introduction

In reactive synthesis, a reactive system implementation is automatically computed from its specification. Such systems continuously interact with their environment (as usual in embedded systems) and synthesis promises to drastically increase developer productivity by freeing her/him from having to write the implementation along with the specification. As a consequence, a developer can then focus on getting the specification correct and complete, which is needed in development processes for safety- or business-critical systems anyway.

There are however two major obstacles that currently prevent the widespread use of reactive synthesis in the field. The first one is scalability: for instance, reactive synthesis from linear temporal logic (LTL) specifications has a doubly-exponential time complexity [33]. This problem is partially mitigated by work on practical reactive synthesis, which spurred substantial performance increases over the last two decades [16]. Furthermore, by focusing on specification formalisms with a lower synthesis complexity that pair well with symbolic reasoning, such as generalized reactivity(1) specifications [8] in combination with binary decision diagrams, the scalability of reactive synthesis can be further improved, which led to the successful execution of the first practical case studies [6, 7, 12, 26]. The corresponding synthesis approach is also commonly called GR(1) synthesis, in which the specifications consist of safety properties (which restrict the transitions allowed by the environment and the system) and liveness properties.

The second obstacle to the widespread use of reactive synthesis in the field is, somewhat unintuitively, a lack of trust in the correctness of the synthesized implementations. While the computed implementations are guaranteed to be correct-by-construction, they are only correct with respect to the specification written by the system’s engineer and not the implicit expectations that the engineer or the later user of the system have [23]. Indeed it has been noticed that the high degree of specification engineering needed for reactive synthesis is a substantial challenge [3, 13, 31]. Ensuring system correctness in reactive synthesis however goes beyond getting the specification right, as a synthesized system typically needs to also convey trust in its correct functioning. Synthesized systems often behave in unexpected ways, for instance by working against their environments [5, 30], which causes several problems. Firstly, if the system is to interact with a human, the human may lose trust in the system if the system behaves in unexpected ways [24]. Similarly, to allow engineers to rely on the correct functioning of a synthesized component when using it in the context of a bigger system, they need to understand why a synthesized system behaves the way it does (also called predictable behavior [36]). Finally, when servicing bigger systems with synthesized components, unexpected system behavior can misinform the error diagnosis of a deployed system. In all three cases, we see that a system’s behavior needs to be understandable for the system to be useful.

This understandability question could be addressed by accompanying the synthesized implementation with a human-readable explanation of how it operates. While the specification sets the boundaries of a synthesized system’s behavior, the synthesis process concretizes them by the strategic choices that a system needs to make to avoid the eventual violation of its specification. These necessary strategic choices, in a sense, represent how the system needs to plan ahead to be able to satisfy the specification regardless of the future input. For instance, a robot controller operating in a shared workspace with humans needs to avoid situations in which it can be trapped by humans, and an explanation of what the robot needs to avoid as a consequence could be a side-output of a synthesis process to document that the system’s behavior is reasonable. This explanation would also help with system integration activities in which the synthesized implementation is used as a component.

But how can an explanation look like? It has to be readable in order to be useful, but readability is a soft target. For systems with a low number of states, the implementation itself (or the set of changes made during the development process to accommodate all specification parts) could be used as an explanation, but especially when synthesizing from generalized reactivity(1) specifications, the generated implementations often need to be large, and the approach is unattractive in such cases. Furthermore, it normally suffices to explain the behavior of the synthesized system, rather than its internal structure. For generalized reactivit(1) specifications, we can however observe that since the safety part of a specification provides boundaries on the transitions that a system can make, the specification itself already provides a large part of the explanation of how a system behaves by defining which transitions are allowed by the system. As the safety specification part is manually written, it needs to be understandable, as otherwise the specifier cannot know if it is correct. What is still left to explain are however the strategic choices that a correct implementation needs to make in order to accommodate all possible future input. In other words, we could explain the system’s behavior by identifying the invariants that a synthesized implementation needs to maintain in addition to satisfying the safety specification. If these are in human-readable form, then we obtain an explanation of the system behavior.

In this paper, we present an approach to identifying invariants that all synthesized implementations need to maintain. We target GR(1) synthesis, which can be reduced to finding a winning strategy in a synthesis game in which the states are exactly the valuations to the input and output variables. We show that in this context, both the strongest and weakest possible invariants are Boolean functions that characterize which input/output combinations must never occur during the execution of a correct system implementation, and these functions are called local input/output invariants in this paper. Since neither the strongest nor weakest local input/output invariants are necessarily easy to interpret, we present an approach to pick and decompose some invariant in between them so that we obtain a minimal number of mixed monotone/antitone invariants. In this class, every invariant consists of a single worst-case variable valuation and a couple of cases that are “also bad”. Every variable valuation that is some mixture of the worst case and a bad case is then also a state to avoid. Our invariant class is quite general and for instance contains all linear inequalities over state variables, including when variables together binary-encode a number.

Our algorithm is intended to be used in the scope of an iterative specification development process, where after each addition to the specification, the engineer checks which invariants the specification entails. These invariants can then be added to the specification as documentation and to restrict the set of reachable states in the game, which also ensures that the invariants are not found again after subsequent specification refinements. As the invariants are added step-by-step and are mixed monotone/antitone, they tend to be comparably simple and understandable, as we demonstrate in two case studies, which concern the synthesis of graphical user interface glue code and a generalized buffer controller [7]. We also demonstrate in the case studies that identifying invariants is a useful specification debugging step: by examining the invariants, an engineer can get some insight into whether the specification is correct and complete.

1.1 Related Work

While invariant identification is a classical task in many sub-fields of formal methods, such as software verification [32], in reactive synthesis, this problem is less explored. Work on inferring (safety) assumptions on the environment that make a specification realizable can however be seen as related [1, 11, 27]. Such assumptions represent a kind of invariant: if the environment fulfills the assumptions all of the time, the system under design also fulfills its specification. The focus in this context is however not on fostering the understandability of a synthesized implementation, but making a specification realizable.

In order to improve the understandability of synthesized systems, one can enforce a certain structure of the solutions. For instance, Lustig and Vardi [28] show how to compute an implementation consisting only of components from a library. Aminof et al. [2] on the other hand show how to generate an implementation consisting of a hierarchy of components, hence enforcing a certain structure. Finally, Khalimov and Jacobs [21] gave an approach to compute parameterized arbitrarily scalable systems. All these approaches however do not come with a guarantee that their results are indeed easy to understand. On a smaller scale, there are also works that aim at making a synthesized finite-state machine easier to understand. For instance, Finkbeiner and Klein [17] presented an approach for synthesizing controllers that are as small as possible while bounding the size of cycles, which allows engineers to understand a controller by looking at small implementation parts in isolation. Baumeister et al. [3] proposed to explain a controller by looking at its evolution when successively adding new specification parts. After each addition, a small repair is generated that explains how the controller had to be changed to accommodate the additional specification part. An implementation can also be made more understandable by making use of typical program structures rather than representing the implementation as a finite-state machine. For instance, a synthesis approach based on two-way automata [18, 29] generates small programs in a simple programming language. However, the scalability of the approach is currently also limited to very small programs. Further related work on synthesizing understandable implementations is given in the survey paper on challenges in reactive synthesis by Torfah and Kress-Gazit [25]. In contrast to all aforementioned work on synthesizing understandable implementations, we focus on synthesizing understandable invariants that augment the specification parts that the system engineer already understands. As such, the approach scales to specifications that require large numbers of states in an implementation, which is commonly the case for generalized reactivity(1) specifications.

The survey paper by Torfah and Kress-Gazit [25] also discusses a wide variety of existing methods for helping with specification debugging in reactive synthesis. The difficulty of writing correct temporal specifications has been acknowledged in multiple application domains, including software [20], hardware design [15] and autonomous system verification [19]. In reactive synthesis, specification debugging is particular important [13], as all relevant properties of a system under design need to be specified in order to guarantee them. Our invariant generation approach augments existing specification debugging techniques because it allows to check for surprising invariants (which indicate problems with the specification) and the absence of expected implied invariants (which indicates that satisfying the new specification part in the intended way can be side-stepped by the implementation).

2 Preliminaries

Sets and Variables: For a finite alphabet \(\varSigma \), we denote the set of finite sequences of \(\varSigma \) as \(\varSigma ^*\) and the set of infinite such sequences as \(\varSigma ^\omega \). We will typically use the Boolean valuations \(2^\mathcal {V}\) to some variable set \(\mathcal {V}\) as alphabet. For notational conciseness, we treat assignments \(x : \mathcal {V} \rightarrow \mathbb {B}\) and subsets of \(\mathcal {V}\) representing the variables with \(\textbf{true}\) values interchangeably. We use 0 and 1 as shorter representatives for the Boolean values \(\textbf{false}\) and \(\textbf{true}\), respectively.

Boolean Formulas: A Boolean formula over some set of variables \(\mathcal {V}\) is in conjunctive normal form if it is a conjunction of clauses, which are disjunctions of literals, which are in turn variables or their negation. Boolean formulas represent Boolean functions, which map variable valuations to \(\textbf{false}\) and \(\textbf{true}\). In the latter case, we call the valuation satisfying. We will sometimes treat a Boolean function as a set of assignments satisfying it. Satisfiability (SAT) solving is the process of checking if a Boolean formula has a satisfying assignment (and computing such a valuation). We say that among a set of Boolean functions \(f_1, \ldots , f_n\), a Boolean function \(f_i\) (for \(1 \le i \le n)\) is the strongest if every model (satisfying assignment) of \(f_i\) is a model of \(f_j\) for all \(1 \le j \le n\). Boolean functions can also be represented as reduced ordered binary decision diagrams (BDDs, [10]). These are acyclic directed graphs with a root node and 0 and 1 as sink nodes. Each non-sink-node n is labeled with a variable \(\textrm{var}(n)\) and has a then-successor \(\textrm{then}(n)\) and an else-successor \(\textrm{else}(n)\). The BDD maps those valuations to \(\textbf{true}\) that include a path from the root node along which the 1 node is eventually reached, where we take the then-successor of a node whenever the variable by which the node is labeled has a \(\textbf{true}\) value in the valuation, and we take the else-successor otherwise. We do not consider the extension of complemented else-edges [9], which some BDD libraries offer, in the presentation of this paper, and on a technical level translate these on-the-fly to BDDs without complemented else-edges for the implementation of the presented approach.

Generalized Reactivity(1) Synthesis: The GR(1) synthesis approach [8] targets a specific class of specifications. Given as input to the synthesis process are disjoint finite sets of input and output variables (also called propositions) \(\textsf{AP}^I\) and \(\textsf{AP}^O\) and a specification \(\varphi \) of the following form:

$$\begin{aligned} \varphi = \left( \varphi ^A_I \wedge \varphi ^A_S \wedge \varphi ^A_L \right) \rightarrow _s \left( \varphi ^G_I \wedge \varphi ^G_S \wedge \varphi ^G_L \right) \end{aligned}$$

The left-hand side of this equation contains the assumptions that, intuitively, encode what environment behavior the implementation to synthesize can assume. The right-hand side of the equation contains the guarantees, which the implementation needs to satisfy. Both assumptions and guarantees are split into:

  • initialization properties, which define the allowed initial values of the propositions \(\textsf{AP}^I\) and \(\textsf{AP}^O\),

  • safety properties, which restrict the possible changes between the valuations of \(\mathcal {V} = \textsf{AP}^I \cup \textsf{AP}^O\) when the system makes a transition, and

  • liveness properties, which define valuations of \(\mathcal {V}\) that should occur infinitely often along a trace of the system.

Traces of synthesized systems are, as usual, finite or infinite sequences of valuations of \(\mathcal {V}\). The \(\rightarrow _s\) symbol in the above GR(1) specification shape denotes strict implication (explained in detail in [22] without naming it), which intuitively means that the implementation must not violate the guarantees before the environment violates its assumptions. More formally, the strict implication holds if along all possible traces \(w = \rho _0 \rho _1 \ldots \in (2^\mathcal {V})^\omega \) of the system, we have:

  1. (a)

    if \(\rho _0 \models \varphi ^A_I\), then \(\rho _0 \models \varphi ^G_I\),

  2. (b)

    if \(\rho _0 \models \varphi ^A_I \wedge \varphi ^G_I\), then for the smallest index \(i \in \mathbb {N}\cup \{\infty \}\) such that \((\rho _i,\rho _{i+1}) \not \models \varphi ^G_S\) or \(i=\infty \), we have that for some \(i'\le i\), we have \((\rho _{i'},\rho _{i'+1}) \not \models \varphi ^A_S\), or

  3. (c)

    if the previous two cases not already define whether a trace satisfies \(\varphi \), then the trace satisfies \(\varphi \) if and only if the trace satisfies \(\varphi ^A_L \rightarrow \varphi ^G_L\).

We say that a specification is realizable if there exists an implementation such that along all of its traces the specification is satisfied. Such an implementation can be represented as a Mealy machine, which induces the system’s executions as its set of traces, where we consider both finite and infinite traces (and their prefixes). For the simplicity of presentation, we assume that a synthesized Mealy machine for a specification \(\varphi \) does not induce traces along which the environment assumptions of \(\varphi \) are violated. As a consequence, there may exist finite traces for the Mealy machine that cannot be extended to an infinite trace of it.

Details on GR(1) synthesis and all further constraints on the specification shape can be found in the paper by Bloem et al. [7], who also presented a synthesis algorithm based on reducing the synthesis problem to solving a game between an input and an output player. The states in this game are the possible valuations to \(\mathcal {V}\), and each state \(v \subseteq \mathcal {V}\) is either losing or winning for the system player, where the latter means that the system player can enforce that a trace starting with v satisfies \( \varphi _{\setminus I} = \left( \varphi ^A_S \wedge \varphi ^A_L \right) \rightarrow _s \left( \varphi ^G_S \wedge \varphi ^G_L \right) \). Determining the winning states W for the system player can be done using Boolean operations over BDDs, and checking realizability then amounts to checking if for all input proposition valuations \(x \subseteq \textsf{AP}^I\) with \(x \models \varphi ^A_I\), there exists some \(y \subseteq \textsf{AP}^O\) such that \((x,y) \models \varphi ^G_I\) and \((x,y) \in W\).

We denote the set of traces that does not violate \(\varphi \) after a finite number of steps by the rules above as \(L_\varphi \). We say that some subset of finite traces \(L \subseteq (2^\mathcal {V})^*\) is a safety property if it is prefix-closed, i.e., for every word \(\rho _0 \ldots \rho _n \in L\) for some \(n \in \mathbb {N}\), we also have \(\rho _0 \ldots \rho _{n-1} \in L\).

3 Computing Mixed Monotone/Antitone Invariants

In this paper, we present an approach to computing invariants that are satisfied by any implementation of a given realizable GR(1) specification. In this context, we only need to consider invariants that restrict the set of input/output combinations that can occur along a system’s trace, as we show next.

Definition 1

Let \(\mathcal {V} = \textsf{AP}^I \cup \textsf{AP}^O\) be a set of atomic propositions and \(\mathcal {M}\) be a Mealy machine. Let us furthermore for some Boolean formula B over \(\mathcal {V}\) define the language \(L_B = \{ \rho _0 \rho _1 \ldots \rho _{n-1} \in (2^\mathcal {V})^* \mid \forall 0 \le i \le n-1.\,\rho _i \models B\}\), i.e., the language over finite words for which each letter in each word in the language satisfies B. We say that B is a local input/output invariant of \(\mathcal {M}\) if every finite trace of \(\mathcal {M}\) is in \(L_B\).

The following lemma proves that for GR(1) specifications, the restriction to local input/output invariants does not restrict the generality of our approach.

Lemma 1

Let \(\mathcal {V} = \textsf{AP}^I \cup \textsf{AP}^O\) and \(\varphi = (\varphi ^A_I \wedge \varphi ^A_S \wedge \varphi ^A_L) \rightarrow _s (\varphi ^G_I \wedge \varphi ^G_S \wedge \varphi ^G_L)\) be a GR(1) specification. There exists a unique strongest Boolean formula B over \(\mathcal {V}\) with the following property: Let \(L \subseteq (2^\mathcal {V})^*\) be a safety property that is satisfied by every trace of every Mealy machine over \((\textsf{AP}^I,\textsf{AP}^O)\) that implements a GR(1) specification \(\varphi \). Then, we have \(L \supseteq L_{\varphi } \cap L_B\), i.e., L is not a stronger invariant than \(L_B\) when considering \(L_\varphi \) as already set.

Proof

Let \(W \subseteq 2^{\mathcal {V}}\) be the set of positions that are winning in the GR(1) game for \(\varphi \). We show that \(B = \bigvee _{i \in \mathbb {N}} B_i\) for \(B_0 = \{v \in W \mid v \models \varphi ^A_I \wedge \varphi ^G_I \}\) and \(B_{i+1} = \{ v' \in W \mid \exists v \in B_i.\, (v,v') \models \varphi ^A_S \wedge \varphi ^G_S\}\) for every \(i \in \mathbb {N}\) has this property. Note that B characterizes the set of winning positions in the synthesis game reachable from some initial winning position while taking only transitions through winning positions. If for some \(j \in \mathbb {N}\), we have \(B_0 \vee \ldots \vee B_j = B_0 \vee \ldots \vee B_{j-1}\), then we know that \(B = B_0 \vee \ldots \vee B_{j-1}\), hence B can be computed in a finite number of steps as there are only finitely many variables in \(\mathcal {V}\).

Let us first show that B is a local input/output invariant. To see this, consider the converse, i.e., there exists a correct implementation having a finite trace \(\rho = \rho _0 \ldots \rho _{n-1}\) such that \(\rho _{n-1} \not \models B\). Let \(\rho \) be a shortest such trace, so for all \(0 \le i < n-1\), we have \(\rho _i \models B\). If now \(\rho _{n-1}\) is not in B, then either (a) \( n > 1\) and \(\rho _{n-1}\) is not reachable from \(\rho _{n-2}\) via a transition satisfying \(\varphi ^A_S\) and \(\varphi ^G_S\), (b) \(\rho _{n-1}\) is not in W, or (c) \(n=1\) and \(\rho _{n-1} \not \models \varphi ^A_I \wedge \varphi ^G_I\). In case (a), the transition from \(\rho _{n-2}\) to \(\rho _{n-1}\) is not an allowed part of a correct implementation, which is a contradiction. In case (b), we have that by the construction of the synthesis game, since \(\rho _{n-1} \notin W\), there exists a strategy for the environment to make \(\varphi _{\setminus I}\) violated when starting with \(\rho _{n-1}\), so the implementation needs to have some trace violating \(\varphi _{\setminus I}\) and which starts with an initial state (satisfying \(\varphi ^A_I \wedge \varphi ^G_I\)), which overall contradicts the satisfaction of \(\varphi \) by the implementation. In case (c), either the assumption from page 6 that along no trace of the Mealy machine, assumptions are violated, does not hold, or \(\varphi ^G_I\) is violated, which means that the specification is violated along the trace.

Finally, let us now prove that \(L \supseteq L_{\varphi } \cap L_B\) holds. Assume that the converse holds, i.e., we have some \(\rho = \rho _0 \ldots \rho _{n-1} \in (2^\mathcal {V})^*\) such that \(\rho \notin L\) but \(\rho \in L_{\varphi } \cap L_B\). An implementation can have \(\rho \) as a (prefix) trace by for the first n steps of its execution choosing arbitrary output allowed by \(\varphi \) while staying in the set of winning positions and after n steps following some winning strategy, which exists from a winning state. As \(\rho \) is in \(L_\varphi \) and has \(\rho _{n-1} \in W\), indeed \(\rho \) can be a prefix trace of a satisfying implementation. But then, L is not an invariant of all implementations, yielding a contradiction.    \(\square \)

This lemma essentially states that for every safety property that every implementation of a specification satisfies along every trace in addition to the specification, we can provide a local input/output invariant achieving the same, and hence if our interest is in computing safety properties that every implementation satisfies in addition to the specification, we only need to compute a strongest possible local input/output invariant. The proof of the lemma above provides a procedure for doing so, which can also be executed with BDDs.

While the computed strongest local input/output invariant B can be represented as a conjunctive or disjunctive normal form Boolean formula or as a BDD to explain what the specification entails, this is often not useful. The set B contains only reachable and winning states, and hence specifications implying a set of reachable positions that is complex to represent often cause B to have a complex representation, too. For invariants that augment the specification, we however have some flexibility regarding which local input/output invariant we choose, which can be exploited.

Definition 2

Let B be the unique strongest local input/output invariant for a given specification \(\varphi \). We say that another Boolean function \(B'\) over \(\mathcal {V}\) satisfies the B-boundary condition if for every \(v,v' \in 2^\mathcal {V}\), if \(v \models B\), then \(v \models B'\), and if furthermore \(v' \not \models B\) and \((v,v') \models \varphi ^A_S \wedge \varphi ^G_S\) hold, we have \(v' \not \models B'\).

Lemma 2

Let \(B'\) be a Boolean function satisfying the B-boundary condition for the strongest local input/output invariant B for a specification \(\varphi \). We have that \(B'\) is also a local input/output invariant satisfied by all implementations of \(\varphi \).

Proof

For a proof by contradiction, assume that there exists an implementation satisfying \(\varphi \) with a trace \(\rho = \rho _0 \ldots \rho _{n-1}\) such that \(\rho _{n-1} \not \models B\), but \(\rho _{n-1} \models B'\). Without loss of generality, let \(\rho \) be a shortest such trace. If \(n=1\), then as \(\rho _0 \models \varphi ^A_I \wedge \varphi ^G_I\), this means that \(\rho _0\) is not a winning position, which contradicts that the implementation satisfies the specification. Otherwise, if \(n>1\), then \(\rho _{n-2} \models B\), but \(\rho _{n-1} \not \models B\). By the definition of B, there are two reasons for why \(\rho _{n-1}\) is not included in B then. If \(\rho _{n-1}\) is not reachable from \(\rho _{n-2}\) while satisfying \(\varphi ^A_S \wedge \varphi ^G_S\), then this contradicts that the implementation satisfies \(\varphi \). The other possible reason is that \(\rho _{n-1}\) is not a winning position in the game, which also contradicts that the implementation under concern satisfies \(\varphi \).    \(\square \)

Note that the Boolean functions \(B'\) satisfying the B-boundary condition are not unique in general, which we can exploit in order to choose one that is easier to represent and to explain. However, there is a weakest such invariant.

Lemma 3

Let \(\{B'_1, \ldots , B'_m\}\) be all possible local input/output invariants satisfying the B-boundary condition for the strongest local input/output invariant B for some specification \(\varphi \). Then we have that \(\tilde{B} = \bigvee _{1 \le i \le m} B'_i\) is also a local input/output invariant for every implementation of \(\varphi \), and the invariant satisfies the B-boundary condition.

Proof

We prove that for a pair of Boolean local input/output invariants \(B'_i\) and \(B'_{i+1}\), we have that \(B'_i \vee B'_{i+1}\) is also a local input/output invariant. The claim then follows by applying this argument \(m-1\) times.

Note that \(B'_i \vee B'_{i+1}\) is indeed a local input/output invariant, as if along any trace of any system satisfying \(\varphi \), we only have letters that are models of \(B'_i\) and we only have letters that are models of \(B'_{i+1}\), then this holds for \(B'_i \vee B'_{i+1}\) by definition.

Now let both \(B'_i\) and \(B'_{i+1}\) satisfy the B-boundary condition. If \(v \models B\), then we know \(v \models B'_i\) and \(v \models B'_{i+1}\), and hence \(v \models B'_i \vee B'_{i+1}\) as well. If furthermore \(v' \not \models B\) and \((v,v') \models \varphi ^A_S \wedge \varphi ^G_S\) hold, then since \(B'_i\) and \(B'_{i+1}\) satisfy the B-boundary condition, we know that \(v' \not \models B'_i\) and \(v' \not \models B'_{i+1}\), and hence \(v' \not \models B'_i \vee B'_{i+1}\).    \(\square \)

Note that \(\tilde{B}\) can be obtained by computing the set of positions \(v'\) that \(\tilde{B}\) must not map to \(\textbf{true}\) according to Definition 2 and choosing its complement as \(\tilde{B}\).

In this paper, we propose a process that computes a local input/output invariant between B and \(\tilde{B}\) (i.e., an invariant \(B'\) such that both \(B \rightarrow B'\) and \(B' \rightarrow \tilde{B}\) are Boolean functions that are equivalent to \(\textbf{true}\)) along with its decomposition into mixed monotone/antitone invariants, which are defined as follows.

Definition 3

Let \(\mathcal {V}\) be a set of variables. We say that a subset of valuations \(I \subseteq 2^\mathcal {V}\) is a mixed monotone/antitone invariant if there exists a worst case valuation w and a set of bad cases \(b^w_0, \ldots , b^w_{m-1}\), all in \(2^\mathcal {V}\) such that the following holds: A valuation \(v \subseteq \mathcal {V}\) has that \(v \not \models I\) if and only if there exists some bad case \(b^w_i\) such that for some selection of variables \(V \subseteq \mathcal {V}\), we have \(v = (w \cap V) \cup (b^w_i \setminus V)\).

The last condition in the definition essentially states that every variable valuation that does not satisfy a mixed monotone/antitone invariant is a mixture of the variable values in the worst case valuation and a corresponding bad case. Note that in the special case of an empty set of bad cases, the mixed monotone/antitone invariant accepts all variable valuations.

Many invariants are mixed monotone/antitone. For instance, the linear inequality \(3 \cdot x_1 + 6 \cdot x_2 - 4 \cdot x_3 - 8 \cdot x_4 \ge 1\) over the variables \(\{x_1,x_2,x_3,x_4\}\) is mixed monotone/antitone. The worst case is the valuation for which the sum on the left-hand side is as small as possible, i.e., \(\{x_3,x_4\}\), and the possible bad cases are the other valuations violating the inequality. In this context, only the valuations \(\emptyset , \{x_1, x_3\}, \{x_1,x_4\}, \{x_2,x_4\}, \{x_1,x_2,x_3,x_4\}\) are actually needed as bad cases, because all other valuations violating the equality can be obtained by mixing one of them with the worst case. In general, every inequality \(f(x_1, \ldots , x_n) \le c\) for some constant c is a mixed monotone/antitone invariant if the function f is either monotone or antitone in every argument. This includes all polynomial inequalities in which the monotone and antitone elements are never mixed.

Not all local input/output invariants \(B'\) are mixed monotone/antitone (such as the exclusive or function between two variables). However, every local input/output invariant \(B'\) can be represented as the conjunction of mixed monotone/antitone invariants. From an algorithmic point of view, we solve the following core problem in this paper:

Definition 4

Let B and \(\tilde{B}\) be Boolean functions over a set of variables \(\mathcal {V}\) and \(m \in \mathbb {N}\). We want to compute a set of mixed monotone/antitone invariants \(I_1, \ldots , I_m\) (along with their worst and bad cases) such that \(B' = I_1 \wedge \ldots \wedge I_m\) for some Boolean function \(B'\) such that \(B \rightarrow B'\) and \(B' \rightarrow \tilde{B}\) each are Boolean formulas that are equivalent to \(\textbf{true}\).

By computing B and \(\tilde{B}\) according to Lemma 1 and Lemma 3, we can use a solution to the problem from Definition 4 to compute a set of m mixed monotone/antitone invariants that together induce a local input/output invariant in between B and \(\tilde{B}\). By first trying to solve this problem for \(m=0\), and increasing m one by one until a solution is found, we can find a smallest possible set of mixed monotone/antitone invariants. Note that for every realizable specification, such a set of invariants can be found – in the worst case, we use a separate mixed monotone/antitone invariant for each valuation not in \(\tilde{B}\).

3.1 Computing a Set of Mixed Monotone/antitone Invariants

Algorithm 1
figure a

. Algorithm for computing n mixed monotone/antitone invariants

Algorithm 1 describes an approach to computing a minimally sized set of mixed monotone/antitone invariants in pseudocode, where for notational simplicity, we assume that the variable set in the specification is \(\mathcal {V} = \{x_1, \ldots , x_n\}\). Lines 1-5 are concerned with computing the state sets B and \(\tilde{B}\) defined above. The remaining lines of the algorithm then solve the problem from Definition 4. For this purpose, we employ counter-example guided inductive synthesis [34] (CEGIS) using an incremental satisfiability (SAT) solver. An empty SAT instance is allocated in line 6 together with the initially empty set of bad cases. The rest of the algorithm consists of the main CEGIS loop. It starts by the SAT solver finding a list of worst cases and an assignment of the bad cases to the worst cases such that no induced mixed monotone/antitone invariant rules out a variable valuation that is a model of B. We employ the following set of SAT variables:

  • The variables \(\{b_{i,j}\}_{1 \le i \le m, 1 \le j \le |\mathcal {V}|}\) are used for the value of variable \(x_j\) in worst case number i.

  • The variables \(\{c_{i,\theta }\}_{1 \le i \le m, \theta \in BAD }\) are used for encoding that bad case \(\theta \) is assigned to the worst case number i.

  • The variables \(\{d_{j,\theta }\}_{1 \le j \le |\mathcal {V}|, \theta \in BAD }\) are used for encoding the worst case valuation that will be used for the bad case \(\theta \).

For the simplicity of presentation, we assume in Algorithm 1 that variables are allocated in the SAT solver on-the-fly. Furthermore, for readability, we use some non-clausal constraints. Since every of them has a fixed number of elements, we can translate them easily without helper SAT variables to clauses in an implementation of Algorithm 1.

Initially, the solver’s SAT instance is empty, so that an arbitrary list of worst cases is computed in line 9. In the following line, the corresponding invariants are computed based on the assignment of bad cases to invariants. If the invariants together cover all states not in \(\tilde{B}\), this is detected in line 11 and the found invariants are returned along with their worst cases.

Whenever it is instead found that some state not in \(\tilde{B}\) still needs to be covered by some invariant, in line 13, a random new bad case is computed. Randomness is used to get a diverse set of bad cases in order to heuristically improve the coverage of \(2^\mathcal {V} \setminus \tilde{B}\) by the mixed monotone/antitone invariants regardless of which worst cases are chosen by the SAT solver.

For the new bad case \(\theta \), in lines 14 and 15, clauses are added that require the SAT solver to assign the bad case to one of the worst cases and to copy the values of the chosen worst case to the variables \(\{d_{i,\theta }\}_{1 \le i \le |\mathcal {V}|}\). Finally, in lines 16 to 19, we encode the check if in a BDD for B, any Boolean valuation between the worst case and the bad case leads to the 1 sink, which would indicate that safe reachable states are ruled out by the mixed monotone/antitone invariant (which is disallowed as we compute invariants that every implementation must fulfill). For this purpose, we use the following set of additional variables:

  • The variables \(\{e_{\xi ,\theta }\}_{\xi \text { is a node in the BDD of } B, \theta \in BAD }\) are used to encode that for some variable valuation between bad case \(\theta \) and its assigned worst case, the BDD node \(\xi \) in the BDD for B is reachable from the root.

When iterating over the BDD nodes of B, the then/else successor of a reachable node is reachable if the respective variable value in the worst case valuation is \(\textbf{true}\)/\(\textbf{false}\), respectively. In case the value of the bad case and the worst case differ, the respective other BDD successor node is also reachable.

If at some point, the SAT solver finds no satisfying assignment, we know that the bad cases cannot be allocated to any set of m worst cases, and then the algorithm terminates without a solution in line 21.

We can also employ a couple of optimizations in addition to the core components of our approach shown in Algorithm 1. First of all, symmetry breaking on the SAT instance can be applied to require the worst case variable valuations to be in lexicographical order. The respective clauses are added once before the main CEGIS loop. Furthermore, whenever for some value of m, no set of invariants is found, in the algorithm run for \(m+1\) invariants, the SAT instance can be bootstrapped by executing lines 14 to 19 for bad examples found previously. Also, input/output variables of the synthesis problem instance that do not appear in the BDD for B can be removed from consideration in the whole algorithm.

We also apply some BDD optimizations to the computed mixed monotone/ antitone invariants to reduce their representation as BDDs (which we give to the user) while keeping their values on all positions in \(B \vee \lnot \tilde{B}\), which are the relevant positions for the correctness of an invariant. This includes trying to existentially or universally quantify variables from the invariant as well as using a BddRestrict optimization on \(B \vee \lnot \tilde{B}\), which heuristically merges some BDD nodes.

4 Experiments and Case Studies

We implemented the approach from this paper as a plugin for the reactive synthesis tool slugs [14], available in a branch with experimental plugins at https://github.com/VerifiableRobotics/slugs/tree/unstable-linuxonly-extensions. The SAT solver CaDiCaL [4] is used for incremental SAT solving and compiled into the slugs executable. We also use the CUDD binary decision diagram library [35]. Computation times (single-threaded) were taken on a computer with an i9-12900H CPU and 32 Gigabyte of RAM.

We demonstrate our invariant computation approach on two case studies. The first case study concerns GUI glue code synthesis [12], while the second one concerns the classical generalized buffer GR(1) synthesis benchmark (from [7]).

4.1 GUI Glue Code Synthesis

When developing programs with a graphical user interface, its developer needs to write event handlers that define how the program reacts in response to events such as button clicks and background computation threads terminating. We consider here a GR(1) specification for the GUI\(\leftrightarrow \)backend interaction for a whiteboard photo postprocessing application, which we modeled after an existing such application that can be downloaded from https://github.com/progirep/BBPhoto. The application has a wizard-like user interface with four views that can be switched between using forward and backward buttons. The overall invariant computation time for this case study is 7.5 s. For the final specification with the invariants, the slugs tool computes an implementation with 172 (explicit) states.

Step 1: We start by specifying GUI behavior on the first view of the wizard, where the user clicks four times to define the boundaries of the screen part showing the whiteboard. The GUI glue code also has to trigger redrawing the view after every click. We also declare the forward and backward buttons to switch between the views. The specification is realizable, and a single invariant BDD over four propositions (shown in Fig. 1a) is computed that states that at every time, only one proposition for the currently selected boundary may have a \(\textbf{true}\) value.

Step 2: In this step, the first parts of the specification for the second view of the wizard are added. The tool computes that no additional invariant is needed.

Step 3: Now, the remaining specification parts for a second wizard view with a preview of the processed whiteboard photo is added. A computation thread for updating the preview is defined, and there are sliders for changing some image processing setting. Whenever a slider moves, the preview update thread needs to eventually run.

Two invariants are generated, shown in Fig. 1b and Fig. 1c. The minimized first invariant is just a 4-literal clause stating that if the second view is shown while the computation thread is running and a state variable tracking if the thread still has to be started has a \(\textbf{false}\) value, the forward button of the wizard has to be enabled. The second invariant encodes that if the computation thread is running or is marked as that it still needs to run, the forward button must not be enabled. The invariants together show an error in the specification, which already has a constraint defining when exactly the forward button is to be enabled—this constraint however erroneously talks about the state before a transition rather than after a transition, and the found invariants identify this oversight.

Step 4: In this step, the third “please wait” view is added that is used when computing the full-resolution image after clicking “next” on the previous view. An additional computation thread is executed while this view is active. As upon completion of the thread, the view of the wizard changes to the next final one, propositions defining the existence of the final view are also added in this step.

A single invariant is computed, with 13 BDD nodes (not counting sinks). During some manual variable reordering, one can find that the BDD tends to get smaller when moving some variables of the one-hot encoding of the current view to the top. We move all of them to the top (as exactly one should have a \(\textbf{true}\) value at every point in time), and we obtain the BDD in Fig. 1d from which we can make observations. The BDD has paths corresponding to the first two views being visible at the same time as well as no view being visible, which indicates that there is no specification part enforcing that this cannot happen. Other than that, the BDD paths leading to the 0 node represent the condition that if the please wait view is shown, the forward button should not be enabled, and if the resolution selection page is shown, the computation thread of the previous view must not be running. Together, these constraints implement the invariant.

Step 5: Additional specification parts are added in this step that represent that on the final view, a thread for computing final images in different resolutions run in the background, and since the threads access the same data, they must not run concurrently. Some other specification parts encode when the new thread need to start. The specification becomes unrealizable, and an analysis reveals that by the user going quickly forward and backwards through the views, she can enforce to start multiple threads at the same time, which is disallowed.

Fig. 1.
figure 1

Some mixed monotone/antitone invariants represented as BDDs. Then-Edges are drawn solid, Else-edges are dashed. Paths to the 0 sink are drawn with gray color (Color figure online).

Step 6: The possibility for the system to deactivate the “back” button is added to address unrealizability. A new invariant is computed whose BDD has 15 nodes. It can be decomposed manually step-by-step by looking at BDD paths to the 0 node, adding specification parts ruling out this path, and recomputing an invariant with a smaller BDD representation to read off the next specification part after each addition. The first constraint states that if the preview computation thread still needs to start after the forward button has been pressed, the backward button needs to be disabled (to avoid the user going back-and-forth). The second invariant part states that if any thread apart from the preview thread is running and the second view is shown, no variable indicating an outstanding thread starting may have a \(\textbf{true}\) value. The third invariant part is more complex and lists cases in which the backward button must not be enabled. All of them have in common that the third view is shown. We realize at this point that a specification part for ensuring that the button is always disabled on the third view is missing and add it as a strengthened invariant.

Finally, we can identify an invariant part that states that whenever the final view is shown, the computation thread of the “please wait” page runs, and the preview thread still needs to be run, then the backward button should not be enabled. This invariant shows that it is possible for an implementation to have background threads of other views running in the final view, which is undesired and indicates that the specification should be changed.

4.2 Generalized Buffer

As a second example, we consider a generalized buffer benchmark with three receivers and two senders, as described by Bloem et al. [7]. Its synthesized implementation has 1098 states. Applying our approach directly on the final specification yields that three mixed monotone/antitone invariants suffice (after 9 min and 20 s of computation time), and 1349 negative examples are enumerated before these invariants are found. The invariant BDDs are however relatively large and have between 58 and 195 nodes. We hence split the specification engineering process into steps again while adding the computed invariants after each step, with an overall computation time of 3.5 s. In three out of the seven steps, invariants are computed and in two of them, the BDDs have four nodes each. The invariants encode that no two acknowledgements to different senders may be given by the buffer controller at the same time as well as being in one of two states of a specification automaton encoded into the specification for certain requests of the buffer’s data receiver. In the remaining step with invariants computed, however, two large invariants BDDs are found. We identified that this is caused by B containing few positions, so that it is possible for the algorithm to spuriously squeeze the reachable but non-winning positions into two invariants. When using W instead of B in line 16 of Algorithm 1 and removing the conjunction with W in line 2, three invariants become necessary (which are found after 1.8 s of computation time), which are at least partially readable (with 10, 38, and 106 nodes). After adding the invariants for the first two BDDs and rerunning the tool, the change in reachable states results in a BDD with 17 nodes for the final invariant, which is then easier to encode.

5 Conclusion

In this paper, we presented an approach to computing a minimal set of mixed monotone/antitone invariants implied by a specification in generalized reactivity(1) synthesis. We provided two case studies that show that the approach is indeed suitable for computing readable invariants in all cases except for the final invariants in the second case study, where we had to compute stronger invariants to improve their representation. The first case study also demonstrates the use of computing invariants for specification debugging. We leave exploiting such invariants to speed up synthesis and utilizing them in order to compute smaller implementations to future work. Furthermore, we note that computing invariants for synthesized implementations that can be used to structure their representation (e.g., as a circuit or as program code) is also still to be explored.

Most of the invariants in the case studies were already readable because our approach performs a decomposition into multiple Boolean functions, hence making each of them more readable. Also, it uses the fact that any invariant between two specific Boolean functions is suitable. Yet, we plan to replace BDDs as invariant representation in future work and to develop solver-based techniques for computing smaller Boolean formula representations for the decomposed invariants.