1 Introduction

Embedded-software systems are growing in number and technical complexity. They are becoming more and more sophisticated towards open, interconnected and networked systems. Due to the complexity of the context in which they operate, their defects can cause life-threatening situations and may have a huge economical impact [19]. In order to improve their efficiency and cost, their quality must be ensured with a rigorous analysis especially for those functions that have safety-critical requirements. Formal architectural models provide an important means to guarantee the correct refinement of system requirements along the design development and decomposition of the system.

Contract-based design, first conceived for software specification by Meyer [26] and nowadays also applied to embedded systems (cfr. e.g., [3,4,5, 15, 17, 18, 22, 29]), is an emerging paradigm for correct-by-construction systems which structures components properties into contracts. A contract specifies the properties assumed to be satisfied by the component environment (assumptions), and the properties guaranteed by the component in response (guarantees). The architecture is analyzed by verifying that each contract of composite components is correctly refined by the contracts of its subcomponents.

In the contract framework proposed in [15, 16], assumptions and guarantees are specified as temporal formulas. Checking the correctness of contracts refinement is supported by generating a set of necessary and sufficient conditions. These proof obligations are temporal formulas obtained from assumptions and guarantees, which are valid if and only if the refinement is correct. The approach is implemented in the OCRA tool [11, 27] and is parametrized by a linear-time temporal logic, either propositional LTL [28], or LTL with SMT predicates [14], or HRELTL [13, 14], a variant of LTL where formulas represent sets of hybrid traces, mixing discrete- and continuous-time steps, and therefore amenable to model properties of hybrid systems. The approach has been used in several contexts and domains. For example, in the FoReVer [2, 20] and AutoFocus [8] framework, the OCRA approach was adopted to formalize requirements into contracts and to specify their refinements along the architecture decomposition in a top-down fashion during the development process. Moreover, a significant case study is presented in [6], where different variants of an industrial-size architectural model of a wheel braking system are analyzed, following the example outlined in the avionic AIR6110 standard.

The approach is very efficient, because the overall correctness proof is decomposed into proofs local to each component. However, the development process for the contract specification and refinement is quite expensive because the requirements are formalised into formal properties, where part of the complexity is delegated to the designer, who has the burden of specifying the contracts. Typical problems include understanding which contracts are necessary for satisfying each contract refinement starting from the system component to the leaf components in a top-down fashion, and how they can be simplified without breaking the correctness of the refinement.

In this article,Footnote 1 we aim to support the development process for the contracts specification and top-down refinement of a system architecture by proposing a technique to simplify the contract refinements during this process. Generally speaking, our approach takes as input a valid contract refinement and attempt to produce a simplified version: first, it finds which subconstracts are relevant for the refinement; second, it relaxes the contracts involved in the refinement by weakening the assumption of the parent component and the guarantees of the subcomponents. This simplification is really useful at the initial stage of the formalization of the contracts both to validate the specification highlighting the relevant parts that are necessary to make the refinement correct and to relax the requirements for subcomponents increasing the design freedom for different implementation solutions. The general technique is called tightening and is based on parameter synthesis. The idea is to generate a set of parametric proof obligations for a single contract refinement, where each parameter evaluation corresponds to a variant of the original contract refinement, and to search for tighter variants of the contracts that still ensure the correctness of the refinement. In more details, the procedure first injects a set of parameters in the contract specification to create a search space of weakened assumptions of the parent contract and guarantees of the subcontracts. We have defined pattern-based functions that take as input a formula and they return a parametric formula and a set of injected parameter. Parameters are injected so that every parameter evaluation yields a respectively weaker or stronger formula. Moreover, we introduce parameters on the contracts in order to extend the parametric problem generated above to determine additionally which subcontracts can be removed from the contract refinement. Then, the intention is to apply this technique step by step on each contract refinement along with the successive decomposition of the system architecture. Note that, it could be the case that diverse contract refinements share some subcontracts which means that the tightened variants of a single contract refinement could possible break the correctness of the others. Consequently, we have developed a robust tightening technique called parallel tightening in order to deal with the problem of sharing of contracts by adding the proof obligations of the other contracts’ refinements that have subcontracts in common. Thereby, we can obtain simplification of the contracts and keep the correctness of all involved contracts refinements.

We cast this approach in the OCRA framework and we evaluate its performance and effectiveness on a number of benchmarks, including the industrial-size architectures described in [6].

Outline The remainder of the paper is structured as follows. In Sect. 2 we introduce some notions used throughout the paper. In Sect. 3, we describe the motivation of using the tightening techniques for supporting the development process for the contracts specification and refinement of a system architecture. We present in Sect. 4 the formal definition and main algorithm for tightening a single contract refinement. In Sect. 5 we describe how tightening of a single contract refinement can be applied iteratively on the different levels of a system architecture and we solve the related issue of tightening contract refinements that share some contracts. We describe the experimental evaluation performed in Sect. 6. In Sect. 7 we discuss the related work. Finally, we discuss in Sect. 8 some conclusions and directions for further work.

2 Background

2.1 Transition systems

Given a finite set \(V\) of variables with a (potentially infinite) domain D, we denote with \(\Sigma (V)\) the set of assignments to \(V\), i.e. mapping from \(V\) to D. Let \(V'\) denote a copy of the variables V, which are used to represent the values of V after a transition.

A transition system (TS) S is a tuple \(S = \langle {V, I, T}\rangle \), where \(V\) is a set of (state) variables, I is a formula over V representing the set of initial states, and T is a formula over \(V\cup V'\) representing the set of transitions. A state \(s \in \Sigma (V)\) of S is an assignment to the variables \(V\).

With abuse of notation, we identify a formula \(\phi \) over V with the set of states that satisfy \(\phi \). Thus, for example, the formula \(\top \) (“true”) represents the set \(\Sigma (V)\). Similarly, we do not distinguish between a formula \(\phi \) over \(V\cup V'\) and the pairs of states (transitions) that satisfy \(\phi \).

A trace \(\sigma \) of S is an infinite sequence of states \(\sigma = s_0, s_1, \ldots \) such that \(s_0 \in I\) and for all \(i\ge 0\), \(\langle {s_i, s_{i+1}}\rangle \in T\). Given two transition systems \(S_1=\langle {V_1, I_1, T_1}\rangle \) and \(S_2=\langle {V_2, I_2, T_2}\rangle \), we define the synchronous product \(S_1\times S_2\) as \(\langle {V_1\cup V_2, I_1\wedge I_2, T_1\wedge T_2}\rangle \). Since the product is commutative and associative, it can be generalized to a set of transitions systems.

2.2 LTL

Given a set of variables \(V\), we assume to be given a set \(Expr(V)\) of Boolean expressions over \(V\) as in [25]. In particular, in this paper we consider standard arithmetic predicates (\(<,\le , >, \ge ,\ldots \)) and functions (\(+,-,\ldots \)) over integer and real variables, although the proposed methods can be applied to more general settings.

We define the set of LTL formulas over the variables \(V\) with the following grammar rule:

$$\begin{aligned} \phi :=p\mid \phi \wedge \phi \mid \phi \vee \phi \mid \lnot \phi \mid X\phi \mid \phi U\phi \mid \phi R\phi \end{aligned}$$

where p ranges in \(Expr(V)\). We use the following standard abbreviations: \(\top :=p\vee \lnot p\), \(\bot :=\lnot \top \), \(\phi \rightarrow \psi :=(\lnot \phi )\vee \psi \), \(F\phi :=\top U\phi \), \(G\phi :=\lnot F\lnot \phi \).

Traces over \(V\) are infinite sequences of assignments to \(V\). Given a trace \(\sigma =s_0,s_1,\ldots \), we denote with \(\sigma [i]\) the \(i+1\)-th state \(s_i\) and with \(\sigma ^i\) the suffix trace starting from s[i].

Given a trace \(\sigma \) and an LTL formula \(\phi \) over \(V\), we define \(\sigma \models \phi \) as follows:

  • \(\sigma \models p\) iff p evaluates to true given the assignment \(\sigma [0]\)

  • \(\sigma \models \phi \vee \psi \) iff \(\sigma \models \phi \) or \(\sigma \models \psi \)

  • \(\sigma \models \phi \wedge \psi \) iff \(\sigma \models \phi \) and \(\sigma \models \psi \)

  • \(\sigma \models \lnot \phi \) iff \(\sigma \not \models \phi \)

  • \(\sigma \models X \phi \) iff \(\sigma ^1\models \phi \)

  • \(\sigma \models \phi U \psi \) iff there exists \(i\ge 0\) s.t. \(\sigma ^i \models \psi \) and for all j, \(0\le j < i\), \(\sigma ^j \models \phi \)

  • \(\sigma \models \phi R \psi \) iff for all \(i\ge 0\) \(\sigma ^i \models \psi \) or there exists j, \(0\le j < i\), s.t. \(\sigma ^j \models \phi \)

The satisfiability problem is the problem of checking if for a given LTL formula \(\phi \) there exists a trace \(\sigma \) such that \(\sigma \models \phi \).

Given a TS \(S=\langle {V,I,T}\rangle \) and an LTL formula \(\phi \) over V, \(S\models \phi \) if for all trace \(\sigma \) of S, \(\sigma \models \phi \). The satisfiability problem of an LTL formula over V can be reduced to model checking by considering the universal model as transition system: i.e., \(\phi \) is satisfiable iff \(\langle {V,\top ,\top }\rangle \not \models \lnot \phi \).

Note that we are considering in general infinite-state transition systems for which these problems are undecidable. Our methods are based on SMT-based algorithms as those implemented in nuXmv [9].

2.3 Parameter synthesis

The goal of parameter synthesis is to find the set of all parameter evaluations for which a given property is satisfied. Let S be a transition system and let U be a set of parameters, we define the parametric transition system \(P = (V, U, I_U, T_U)\), where \(I_U\) is a formula over \(V \cup U\) and \(T_U\) is a formula over \(V \cup U \cup V'\). We define the parameters as frozen, i.e., we set their value in the initial state and preserve it during the execution of the system. Given a valuation for the parameters (\(\gamma \in \Sigma (U)\)), and a formula \(\psi \) we write \(\gamma (\psi ) = \psi [u/\gamma (u)]_{u \in U}\), to indicate that each parameter has been substituted with its value. Given a parametric transition system P and a valuation for the parameters \(\gamma \), we can compute the induced transition system, by replacing the parameters with their valuation: \(P_\gamma = (V, \gamma (I_U), \gamma (T_U))\). Given an LTL property \(\phi \) expressed over the state variables and parameters, the parameter region \(\rho \) is the set of assignments to the parameters such that the property is satisfied by every trace of the induced system, formally: \(\rho = \{ \gamma \mid P_\gamma \models \gamma (\phi ) \}\).

In this paper, we consider Boolean parameters and, with abuse of notation, we identify a parameter evaluation \(\gamma \) with the set \(\{p\mid p\in U,\gamma (p)=\top \}\). The parameter region is monotonic iff whenever \(\gamma \subseteq \gamma '\), if \(\gamma \in \rho \) then \(\gamma '\in \rho \). The monotonicity of the parameter region is typically exploited by parameter synthesis algorithms that enumerate the parameter evaluations \(\gamma \) such that \(P_\gamma \not \models \gamma (\phi )\). In fact, one can proceed with \(\gamma \) of increasing cardinality and as soon as \(P_\gamma \models \gamma (\phi )\) all \(\gamma '\) with \(\gamma \subseteq \gamma '\) can be included in \(\rho \). Thus, in case of monotonicity, the parameter region can be represented by the set of minimal sets \(\gamma \) such that \(P_\gamma \models \gamma (\phi )\) (taking implicitly the upward closure).

2.4 Contract refinement

In order to simplify the presentation, in this paper, we define a contract refinement independently from the component interfaces. In practice, in the tool support we consider, contracts are specified in terms of component input/output ports and the refinement has to take into account the connections among ports in component decomposition.

A contract C over the variables \(V\) is a pair \(\langle {\mathsf {A}, \mathsf {G}}\rangle \) of LTL formulas over \(V\) representing respectively an assumption and a guarantee. We also denote \(\mathsf {A}\) by \(\mathcal {A}(C)\), \(\mathsf {G}\) by \(\mathcal {G}(C)\), and \(\lnot \mathsf {A}\vee \mathsf {G}\) by nf(C). Let \(C=\langle {\mathsf {A}, \mathsf {G}}\rangle \) be a contract over \(V\). Let I and E be TS over \(V\). We say that I is a correct implementation of C iff \(I\models {\mathsf {A}}\rightarrow {\mathsf {G}}\). We say that E is a correct environment of C iff \(E\models {\mathsf {A}}\). We denote by \(\mathcal {I}(C)\) and \(\mathcal {E}(C)\), respectively, the set of correct implementations and the set of correct environments of C. Given two contracts C and \(C'\) over \(V\), we say that C refines \(C'\) (denoted by \(C\preceq C'\)) iff \(\mathcal {I}(C')\subseteq \mathcal {I}(C)\) and \(\mathcal {E}(C)\subseteq \mathcal {E}(C')\).

Fig. 1
figure 1

Contract-based design flow

In a system architecture, each contract is associated to a component. If a component is decomposed into subcomponents, the associated contracts of the parent component are implemented by the composition of the subcomponents’ implementations. Similarly, the environment of the contract of a subcomponent is given by the composition of the environment of the composite component and the implementations of the other subcomponents. In order to prove that such decomposition is correct, we generalize the refinement notion to a set of contracts.

Given a contract C and a set of contracts \({Sub}=\{C_1,\ldots ,C_n\}\), we say that \({Sub}\) is a refinement of C, written \({Sub}\preceq C\), iff the following conditions hold:

  1. 1.

    the correct implementations of the sub-contracts form a correct implementation of C: let V be the variables of C,

    $$\begin{aligned} \{S_1\times \cdots \times S_n\mid S_1\in \mathcal {I}(C_1),\ldots ,S_n\in \mathcal {I}(C_n)\}\subseteq \mathcal {I}(C) \end{aligned}$$
  2. 2.

    for every \(C_i\in {Sub}\), the correct implementation of the other sub-contracts and a correct environment of C form a correct environment of \(C_i\): let \(V_i\) be the variables of \(C_i\),

    $$\begin{aligned}&\{E\times S_1\times \cdots \times S_{j\ne i}\times \cdots \times S_n\mid \\&E\in \mathcal {E}(C),\quad \text { for all }\quad j,1\le j\le n,j\ne i, S_j\in \mathcal {I}(C_j)\}\subseteq \mathcal {E}(C_i) \end{aligned}$$

In [15, 16], we proved that the refinement is correct if and only if the following proof obligations (\(PO({Sub}, C)\)) are valid temporal formulas:

$$\begin{aligned}&nf(C_1)\wedge \cdots \wedge nf(C_n)\rightarrow nf(C)\\&\mathsf {A}\wedge \bigwedge _{1\le j\le n,j\ne i} nf(C_j)\rightarrow \mathsf {A}_i \text { (for every }i, 1\le i\le n) \end{aligned}$$

3 Motivation

3.1 Contract-based design

The contract-based design flow considered in this paper is depicted in Fig. 1, using the example of a control system with a redundant sensor. The example is taken from a case study developed in the FoReVer project [2, 20]. This simple control example takes as input a value, speed, representing the physical speed of the system and returns a brake signal, brake. The system is decomposed into a RedundantSensor that reads the physical value and a ControlUnit that reads the speed from the sensor and produces corresponding control signals to the brakes. As the name suggests, the RedundantSensor is implemented with two redundant Sensors, so that the system works even if one of the sensor fails. In order to analyze the behavior of the system in the presence of failures, we introduce two inputs, fail1 and fail2, which represent the failures of the two sensors.

The design starts with the view of the ControlSystem as a whole black box with ports to interact with its environment. Then, it is decomposed into RedundantSensor and ControlUnit components. The RedundantSensor is in turn decomposed into two redundant Sensor subcomponents, two Monitor subcomponents, and a Selector. The decomposition also defines how the ports of the component being decomposed are mapped down into the decomposition. For example, the “left” ports of the ControlSystem are mapped onto the “left” ports of the RedundantSensor. At the lower level, speed and fail1 are mapped to the input of first sensor, while the inputs of the second sensor are given by speed and fail2.

Each component in the hierarchy is associated with a set of contracts, depicted in green, specifying the acceptable behaviors for the component and its environment. So, for example, ControlSystem has a contract speed_control consisting of the guarantee \(G(\texttt {speed}\ge ~th \rightarrow \texttt {brake})\), where th is a threshold parameter of the system, and the assumption \(\lnot \texttt {fail1}\wedge \lnot \texttt {fail2}\wedge G(\lnot \texttt {fail1}\vee \lnot \texttt {fail2})\), i.e., initially none of the sensors is failing while in every future state it sufficient that one of the two is not failing.

Contracts are refined, following the decomposition of components. For example, the contracts of the ControlSystem are refined by some contracts of the RedundantSensor and the ControlUnit subcomponents. The framework guarantees that, under specific conditions (corresponding to correct contract refinement), if the contracts of the subcomponents hold, then the contract of the parent component also holds.

The contract-based top-down development process can be schematized with the following loop (as reported in the report [2] of the FoReVer project):

  1. 1.

    select a component (let us call it S) without subcomponents and decompose it specifying the children subcomponents;

  2. 2.

    for each type of the defined subcomponents, declare the component and its input and output ports;

  3. 3.

    detail the decomposition of the component S chosen at step 1 defining the connections among the subcomponents and the delegation connections between S and the subcomponents;

  4. 4.

    for each type of the subcomponents, specify the contracts that are necessary to fulfil the contracts of S;

  5. 5.

    for each contract C of S specify the refinement relationship, i.e., which contracts of the subcomponents refine C;

  6. 6.

    check the refinement with OCRA and, if not correct, adjust the contracts in order to remove all issues in the refinement;

  7. 7.

    if there are no more components to be refined, terminate successfully; otherwise, go to 1.

3.2 Need of tightening

The general development process for the contracts specification and refinement has been successfully applied in several frameworks [2, 8] and on realistic case studies like in [6]. However, the process is quite expensive because the requirements are formalized into formal properties, where part of the complexity is delegated to the designer, who has the burden of specifying the contracts. It is clear that there is a missing of analysis methods for the improvement of contract understanding during the development process for the contracts specification and refinement. Automatic synthesis of the contract specification would be ideal for this purpose, for example, if we have a partial specification of a contract refinement, we can automatically synthesize the assumption/guarantee of one of the subcontracts and thus understand in more details the contract specification. However, automatic synthesis is not in general feasible especially in the case of first-order formulas. Therefore, we aim at providing at least support when the contract refinement is manually specified.

Consider the ControlSystem example above. The Selector component has a contract with assumption “true” and guarantee:

$$\begin{aligned}&\texttt {current\_use}=1 \wedge \\&G((\texttt {current\_use}=1 \wedge \texttt {switch\_current\_use}) \rightarrow X(\texttt {current\_use}=2)) \wedge \\&G((\texttt {current\_use}=2 \wedge \texttt {switch\_current\_use}) \rightarrow X(\texttt {current\_use}=1)) \end{aligned}$$

where current_use is 1 or 2 depending on whether the output is linked to the output of sensor1 or sensor2 respectively, and switch_current_use triggers the change of current_use when the monitor of the current sensor detects a failure. Notice, however, that initially the current sensor is sensor1 and therefore there no need to assume (in the top-level assumption) that \(\lnot \) fail2 (i.e., that initially sensor2 does not fail).

As another example, consider a different system assumption on the occurrence of sensor faults:

$$\begin{aligned} \lnot \texttt {fail1}\wedge (G(\lnot \texttt {fail1})\vee G(\lnot \texttt {fail2})) \end{aligned}$$

i.e., we assume that we cannot have both sensors eventually failing. Since initially the selector is using sensor1 and switch_current_use only triggers the change of current_use to sensor2 when the monitor of sensor1 detects a failure, given the stronger assumption, it will never switch back to sensor1. Thus, the part \(G((\texttt {current\_use}=2 \wedge \texttt {switch\_current\_use}) \rightarrow X(\texttt {current\_use}=1))\) of the above guarantee is not necessary.

In general, it may happen to specify contracts on the subcomponents that are more demanding than necessary or that contain unwanted redundancies. It may happen also that, the designer specifies a very strong assumption on the system to make the refinement correct and later wants to relax the assumption while keeping the design correct. More generally, given a correct contract refinement, we would like to understand if the guarantees of subcomponents and assumption on the composite component can be weakened. Finally, the designer could include irrelevant subcontracts on a contract refinement that can be removed. Then, we would also like to provide the designer with information regarding which contracts are sufficient for (or prevent) correct refinement. We address all these issues by defining a problem called top-down tightening of a contract refinement.Footnote 2

4 Tightening of a single contract refinement

In this section, we present the formal definition of local tightening for a single contract refinement and the algorithms for computing a top-down tightening of a given contract refinement.

4.1 Formal definition

We now define formally the problem of tightening a contract refinement as follows. Given a contract C, and a set of contracts Sub such that \(Sub \preceq C\), a tightening of this contract refinement is given by a contract \(C'\), a subset \(\underline{Sub}\) of Sub, and a set of contracts \(\underline{Sub}' = \{C'_S~|~ C_S\in \underline{Sub}\}\) such that:

  • \(\underline{Sub}' \preceq C'\)

  • \(C'\preceq C\) and, for every \(C_S\in \underline{Sub}\), \(C_S \preceq C'_S\).

A top-down tightening is a tightening as defined above such that \(\mathcal {G}(C)=\mathcal {G}(C')\) and, for all \(C_S\in \underline{Sub}\), \(\mathcal {A}(C_S)=\mathcal {A}(C'_S)\). We can easily prove that, equivalently, a top-down tightening is given by a contract \(C'\), a subset \(\underline{Sub}\) of Sub, and a set of contracts \(\underline{Sub}' = \{C'_S~|~ C_S\in \underline{Sub}\}\) such that:

  • \(\underline{Sub}' \preceq C'\)

  • \(\mathcal {A}(C)\models \mathcal {A}(C')\) and, for every \(C_S\in \underline{Sub}\), \(\mathcal {G}(C_S)\models \mathcal {G}(C'_S)\).

4.2 The algorithm

We present now the main algorithm for top-down tightening of a contract refinement which takes as input a valid contract refinement \({Sub}\preceq C\) and produces as output a number of tightened versions of the given contract and its subcontracts. The procedure first injects a set P of parameters in the contract specification to create a search space of weakened assumptions of the parent contract C and guarantees of the subcontracts in \({Sub}\). Second, it creates the related proof obligations that are now parametrized by P. Additionally, we inject extra parameters on each contract in order to detect which contracts are sufficient for correct refinement. Then, we want to find for which configurations of the parameters the contract refinement holds. This is a multiple parameter synthesis problem, because we have to search for the assignment to P such that all proof obligations are valid. Thus, as third step, we propose two alternative ways to solve this problem (see Sect. 4.4). In the first step, we make sure that the injection of parameters creates a monotonic parameter region by construction, which can be exploited by the synthesis algorithm.

figure a

These steps are formalized as follows, while the pseudo-code is shown in Algorithm 1. Suppose we want to obtain a top-down tightening of \({Sub}\preceq C\).

  1. 1.

    We transform C and \({Sub}\) (line 2) into parametrized versions \(C^P\) and \({Sub}^P=\{C_S^P\mid C_S\in {Sub}\}\) with parameters P such that for every evaluation \(\gamma \) of P, if \(\gamma ({Sub}^P)\preceq \gamma (C^P)\), then \(\langle {\gamma (C^P),\gamma ({Sub}^P)}\rangle \) is a top-down tightening of \(\langle {C,{Sub}}\rangle \).

  2. 2.

    We extend the construction of the proof obligations (line 5) of \(\gamma ({Sub}^P)\preceq \gamma (C^P)\) by injecting further parameters \(P_{Sub}=\{p_{C_S}|C_S\in {Sub}\}\), one for each subcontract \(C_S\in {Sub}\), such that for every evaluation \(\gamma \) of \(P\cup P_{Sub}\), \(\gamma (\underline{{Sub}}^P_\gamma )\preceq \gamma (C^P)\) where \(\underline{{Sub}}^P_\gamma =\{C_S^P\mid \gamma (p_{C_S})=\top \}\).

  3. 3.

    We solve the multiple parameter synthesis problem (line 8) by either:

    1. (a)

      finding the region \(W_\phi \) for each proof obligation and then intersecting such sets of parameter, i.e., \(W = \{\gamma \in \Sigma (P)\) s.t. \(\models \gamma (\phi (V,P))\) for all \(\phi \in PO(V,P)\} = \bigcap _{\phi \in PO} W_\phi \); or

    2. (b)

      encoding it into an equivalent proof obligation \(\phi _{PO}\) so that \(\{\gamma \in \Sigma (P)\) s.t. \(\models \gamma (\phi )\) for every \(\phi \in PO(V,P)\}=\{\gamma \in \Sigma (P)\) s.t. \(\models \gamma (\phi _{PO})\}\).

Note that the output of the algorithm produces a set of tightened variants of the given contract refinement. The designer has to analyze these results and may decide to substitute the current specification with one of the tightened versions. Moreover, providing the different ways in which the specification can be simplified may improve the understanding of why the contract refinement is correct.

4.3 Generation of the parametric problem

4.3.1 Injecting parameters in the formulas

In this subsection, we describe how we introduce parameters in the formulas that define the assumption and guarantee of the contracts and generate a monotonic parameter synthesis problem. The high-level transformation is described in Algorithm 2 for top-down tightening of \({Sub}\preceq C\) where the assumption of the parent contract C and guarantees of the subcontracts in \({Sub}\) are weakened.

figure b

The Weaken and Strengthen functions are described respectively in Algorithms 3 and 4. They take as input a formula and they return a parametric formula and a set of injected parameters. The definition assumes that every new parameter p is a fresh symbol. The number of parameters is linear in the size of the formula.

Parameters are injected so that every parameter evaluation yields a respectively weaker or stronger formula.

We remark that we do not aim to obtain the weakest or strongest version of a formula. In our approach, the definition of Weaken and Strengthen functions is pattern-based where new patterns can be investigated to complement or improve the current ones.

In the following, we use \(\phi ^W\) to denote the formula injected with parameters by Weaken (i.e., \(\textit{Weaken}(\phi )=\langle {\phi ^W,P}\rangle \)) and with \(\phi ^S\) the formula injected with parameters by Strengthen (i.e., \(\textit{Strengthen}(\phi )=\langle {\phi ^S,P}\rangle \))

Theorem 1

For any parameter evaluation \(\gamma \), \(\phi \rightarrow \gamma (\phi ^W)\) and \(\gamma (\phi ^S)\rightarrow \phi \).

Proof

We prove the theorem by induction on the structure of the formula. The result of \(\textit{Weaken}\) and \(\textit{Strengthen}\) is outlined in Tables 1 and 2. It is routine to check line by line on the fourth column of the tables that, for every \(\gamma \), \(\phi \rightarrow \gamma (\phi ^W)\) and \(\gamma (\phi ^S)\rightarrow \phi \), based on the inductive hypothesis that \(\phi _1\rightarrow \gamma (\phi ^W_1)\), \(\phi _2\rightarrow \gamma (\phi ^W_2)\), \(\gamma (\phi ^S_1)\rightarrow \phi _1\), and \(\gamma (\phi ^S_2)\rightarrow \phi _2\). Note, in particular, that the weakening of \(\lnot \phi _1\) is given by \(\lnot \phi ^S_1\) and thus \(\lnot \phi _1\rightarrow \lnot \gamma (\phi ^S_1)\) because by induction \(\gamma (\phi ^S_1)\rightarrow \phi _1\). Similarly for strengthening \(\lnot \phi _1\). \(\square \)

It follows immediately that Algorithm 2 yields a correct top-down tightening, as stated in the following corollary.

Corollary 1

Let C be a contract and \({Sub}\) a set of contracts. Let \(\langle {\langle {{Sub}^P,C^P}\rangle ,P}\rangle \) be the result of \(TopDownTightening({Sub},C)\). Then, for any evaluation \(\gamma \) of the parameters P, if \(\gamma ({Sub}^P)\preceq \gamma (C^P)\) then \(\langle {\gamma ({Sub}^P),\gamma (C^P)}\rangle \) is a top-down tightening of \(\langle {{Sub},C}\rangle \).

Table 1 Simplification table for \(\textit{Weaken}(\phi )\)
Table 2 Simplification table for \(\textit{Strengthen}(\phi )\)

Moreover, the parameter injection is designed so that the semantics of the parametric formulas is monotonic with respect to the parameter evaluations.

Theorem 2

If \(\gamma \subseteq \gamma '\), then \(\gamma '(\phi ^W)\rightarrow \gamma (\phi ^W)\) and \(\gamma (\phi ^S)\rightarrow \gamma '(\phi ^S)\).

Proof

Looking again at Tables 1 and 2, one can check the monotonicity case by case. In fact, for each type of formula, the lines report the result of \(\textit{Weaken}\) and \(\textit{Strengthen}\) sorted according to the strength of the parameter evaluation (third column). More precisely, if \(\gamma \) is below \(\gamma '\), then either they are incomparable or \(\gamma \subset \gamma '\). Therefore it is routine to prove that, in the second case, \(\gamma '(\phi ^W)\rightarrow \gamma (\phi ^W)\) and \(\gamma (\phi ^S)\rightarrow \gamma '(\phi ^S)\) (fourth column). \(\square \)

figure c

Note that parameters are introduced per contract, so they are shared by different occurrences of the assumption/guarantee in the proof obligations. It is immediate to show that, thanks to the structured way in which formulas are either strengthened or weakened, the resulting synthesis problem is monotonic, as stated in the following corollary.

Corollary 2

Let C be a contract and \({Sub}\) a set of contracts. Let \(\langle {\langle {{Sub}^P,C^P}\rangle ,P}\rangle \) be the result of \(TopDownTightening({Sub},C)\). Then, for any evaluation \(\gamma , \gamma '\) of the parameters P such that \(\gamma \subseteq \gamma '\), if \(\gamma ({Sub}^P)\preceq \gamma (C^P)\) then \(\gamma '({Sub}^P)\preceq \gamma '(C^P)\).

figure d

4.3.2 Injecting parameters on the contracts

In this subsection, we describe how we introduce parameters on the contracts in order to extend the parametric problem generated above to determine additionally which subcontracts can be removed from the contract refinement. This is done as part of the procedure ExtendedPO called in Algorithm 1.

The main goal here is to generate a parametric problem to be able to find a possibly smaller subset of subcontracts that still refines the parent contract, that is, given a contract C, and a set of contracts \({Sub}\) such that \({Sub}\preceq C\), we want to find a set \(\underline{{Sub}}\subseteq {Sub}\) such that \(\underline{{Sub}} \preceq C\).

We define formally ExtendedPO as follows. Given a contract C, a set of contracts Sub, and a set of parameters \(P_{Sub}\) (\(|P_{Sub}| = |{Sub}|\), i.e., one new parameter for each subcontract in \({Sub}\)), the extended set of proof obligations (PO) with extra parameters is defined as follows:

  • The extended proof obligation for satisfying the top level contract

    $$\begin{aligned} \bigwedge \limits _{i = 1}^{n} (p_i \rightarrow (\mathsf {A}_i \rightarrow \mathsf {G}_i)) \rightarrow (\mathsf {A}\rightarrow \mathsf {G}) \end{aligned}$$
  • For each \(1 \le j\le n\), we have n extended proof obligations:

    $$\begin{aligned} \Big (\bigwedge \limits _{i=1 \wedge i \ne j}^{n} (p_i \rightarrow (\mathsf {A}_i \rightarrow \mathsf {G}_i)) \wedge \mathsf {A}\Big ) \rightarrow (p_j \rightarrow \mathsf {A}_j) \end{aligned}$$

The idea is that we inject activation variables, one for each subcontract, which are used for activating the corresponding contract \(C_i\). We then construct the proof obligations and at the same time we perform an extension of these proof obligations so that these extensions encode the problem of finding \(\underline{Sub}'\).

Note that if \(p_j\) is assigned to false, the whole proof obligation concerning the entailment of the assumption \(\mathsf {A}_j\) is equivalent to trivially true. Such a parameter, however, makes that proof obligation non-monotone. So, in this case, the synthesis engine may be less efficient.

In any case, the result is correct, as proved in the following theorem, i.e. for every parameter evaluation on ExtendedPO, the resulting contract refinement is correct.

First, let us define the subset of contracts \({Sub}\) corresponding to a given parameter evaluation \(\gamma \) as \({Sub}_{\gamma } = \{C_S\in {Sub}~|~\gamma (p_{C_S}) = \top \}\).

Theorem 3

Let \(\langle {\langle {{Sub}^P, C^P}\rangle , P}\rangle \) the result of \(TopDownTightening({Sub}, C)\) for a given contract C and a set of contracts \({Sub}\). Let \(P_{Sub}\) be a set of parameters containing one new parameter for each subcontract in \({Sub}^P\). Then, for all parameter evaluation \(\gamma \), \(\gamma (ExtendedPO(\langle {{Sub}^P, C^P}\rangle , P_{Sub}))\) and \(PO (\gamma ({Sub}^P_{\gamma }), \gamma (C^P))\) are equivalent.

Proof

We prove that if \(\phi \in ExtendedPO(\langle {{Sub}^P, C^P}\rangle , P_{Sub}))\), then either \(\gamma (\phi )\) is valid or there exists an equivalent proof obligation in \(PO (\gamma ({Sub}^P_{\gamma }), \gamma (C^P))\).

Suppose \(\phi \) is the extended proof obligation for the top level contract, that is, \(\bigwedge _{1\le i\le n} (p_i \rightarrow (\mathsf {A}_i \rightarrow \mathsf {G}_i)) \rightarrow (\mathsf {A}\rightarrow \mathsf {G})\). Then, \(\gamma (\phi )\) is equivalent to \(\bigwedge _{1\le i\le n,\gamma (p_i)=\top } \gamma ((\mathsf {A}_i \rightarrow \mathsf {G}_i) \rightarrow (\mathsf {A}\rightarrow \mathsf {G}))\), which belongs to \(PO (\gamma ({Sub}^P_{\gamma }), \gamma (C^P))\).

Suppose \(\phi \) is the extended proof obligation of the \(C_S\) assumption, where \(C_S\in {Sub}\). Thus, \(\phi =\bigwedge _{1\le i\le n} (p_i \rightarrow (\mathsf {A}_i \rightarrow \mathsf {G}_i)) \rightarrow (\mathsf {A}\rightarrow p_j\rightarrow \mathsf {A}_j)\). If \(\gamma (p_j)=\bot \), then \(\gamma (\phi )\) is equivalent to true. If instead \(\gamma (p_j)=\top \), then, \(\gamma (\phi )\) is equivalent to \(\bigwedge _{1\le i\le n,\gamma (p_i)=\top } \gamma ((\mathsf {A}_i \rightarrow \mathsf {G}_i) \rightarrow (\mathsf {A}\rightarrow \mathsf {A}_j))\), which belongs to \(PO (\gamma ({Sub}^P_{\gamma }), \gamma (C^P))\).

Similarly, we can prove that for every \(\psi \) in \(PO (\gamma ({Sub}^P_{\gamma }), \gamma (C^P))\), there exists \(\phi \in ExtendedPO(\langle {{Sub}^P, C^P}\rangle , P_{Sub})\) such that \(\psi \) is equivalent to \(\gamma (\phi )\). \(\square \)

4.4 Multiple validity parameter synthesis problem

The approach to solve the tightening problem proposed in Sect. 4.2 introduces the problem of finding the parameter evaluations \(\gamma \) such that each formula \(\phi (P,V)\in PO\) instantiated with \(\gamma \) is valid. In other words, we want to find for which configurations of the parameters the contract refinement holds. Each validity problem can be reduced to a model checking problem but the parameter evaluation is shared by the different verification problems. This is distinct from the standard parameter synthesis problem where only one verification problem is considered. In fact, we have to search for the assignment to P such that all proof obligations are valid. We called this problem a multiple validity parameter synthesis problem (to be not confused with multiple objective parameter synthesis problem). In the following subsections we propose two approaches to deal with this problem, which are alternative solutions for the function SolveMultipleParamSyntProblem(POP) called in Algorithm 1.

4.4.1 Compositional approach

The simplest solution is to find the parameter region for each proof obligation and calculate the intersection of the results. The correctness of the approach is formalized by the following theorem.

Theorem 4

If \(W = \{\gamma \in \Sigma (P)\) such that \(\models \gamma (\phi (V,P))\) for all \(\phi \in PO(V,P)\}\) and \(W_\phi =\{\gamma \in \Sigma (P)\) such that \(\models \gamma (\phi (V,P))\}\), then \(W = \bigcap _{\phi \in PO} W_\phi \).

Proof

It trivially derives from the definition of sets intersection. \(\square \)

4.4.2 Encoding all proof obligations into a single one

As an alternative approach, we propose to reduce the multiple validity problems to one validity problem by renaming the variables in \(V\) and taking the conjunction of the proof obligations. Namely, if \(PO=\{\phi _1,\ldots ,\phi _n\}\) we create the formula \(\phi _{PO}(P,V_1,\ldots ,V_n)=\bigwedge _{1\le j\le n}\phi _j[V_j/V]\), where \(V_j\) contains one copy \(v_j\) for each variable \(v\in V\) and \(\phi _j[V_j/V]\) is the formulas obtained by substituting every variable \(v\in V\) with \(v_j\) (while the parameters P remain unchanged).

Theorem 5

For all parameter evaluation \(\gamma \), \(\gamma (\phi _{PO})\) is valid iff, for all formulas \(\phi \in PO\), \(\gamma (\phi )\) is valid.

Proof

\(\Rightarrow \)) Suppose for some \(\phi _j\in PO\), \(\gamma (\phi _j)\) is not valid. Let \(\sigma \) be a trace over \(V\) satisfying \(\lnot \gamma (\phi _j)\). Let us define the trace \(\sigma _j\) such that, for every \(i\ge 0\), for all \(v\in V\), \(\sigma _j[i](v_j)=\sigma [j](v)\). Let us extend \(\sigma _j\) to a trace \(\sigma '_j\) over \(V_1\cup \cdots \cup V_n\) assigning variables not in \(V_j\) in an arbitrary way. Then \(\sigma '_j\) satisfies \(\lnot \gamma (\phi _{PO})\).

\(\Leftarrow \)) Suppose for \(\phi _{PO}\) is not valid. Let \(\sigma \) be a trace over \(V_1\cup \cdots \cup V_n\) satisfying \(\lnot \gamma (\phi _{PO})\). Then, there exists j, \(1\le j\le n\), such that \(\sigma \models \lnot \gamma (\phi _j[V_j/V])\). Let us define the trace \(\sigma _j\) such that, for every \(i\ge 0\), for all \(v\in V\), \(\sigma _j[i](v)=\sigma [j](v_j)\). Then \(\sigma '_j\) satisfies \(\lnot \gamma (\phi _j)\). \(\square \)

4.4.3 Minimal configuration of the parameters

For each parameter evaluation, we should present to the user the corresponding tightened contract refinement. Since the parameter region may be really large, it is not feasible to present all possible configurations and we need to select some representative cases. In fact, we present only minimal configurations, assuming monotonicity. This is an under-approximation and in case the parameter region is not upward closed (non-monotonicity), there may be supsersets (less tightened versions) that are not in the paremeter region. However, all presented configurations are correct tightening (and thus correct contract refinements).

In practice, we use an off-the-shelf algorithm, reported in [7], to compute the parameter region which generates the minimal configurations of the parameters under the monotonicity assumption. In the compositional approach, we compute the parameter region for each proof obligation by calling each time the off-the-shelf algorithm. After that, we intersect such sets of minimal configurations of the parameters. Additionally, we compute the prime implicants of the intersection result to obtain the minimal configurations of the parameters such that all proof obligations are valid. In the second solution, the problem is reduced to one validity problem which means that only one time the off-the-shelf algorithm is called and it is obtained the minimal parameters of the region.

We would like to remark also that we have defined two alternatives solutions considering different encodings of the problem and also taking into account the number of times to be call the parameter synthesis algorithm reported in [7] which is used as back-end for computing the parameter region. Note that we obtain the same parameter region as result for each contract refinement with both approaches. It is clear that the encoding of all proof obligations into a single one is syntactically more complex than the composition approach. Moreover, the off-the-shelf algorithm is called only one time for computing the parameter region, contrary to the compositional approach which is called each time for each proof obligation. Furthermore, we have to integrate each result as was explained above such that all proof obligations are valid. In Sect. 6, we compare the performance of both approaches.

5 Tightening the whole system architecture

5.1 Applying tightening within a system architecture

Let us now explain how we can apply the single tightening technique on the whole system architecture during the development process for the contract specification and refinement. Let us take the architecture example from Fig. 1. We start from the system component ControlSystem, where only the contract speed_control is defined. This contract is refined by (1) the contract sense of the redundant sensor, which guarantees that the value passed to the control unit approximates the physical value with a bounded error; (2) the contract sensed_speed_is_present of the redundant sensor, which guarantees that the value provided by the sensor is always available; (3) the contract speed_control, which similarly to the system contract guarantees that if the speed is above the threshold, then a brake command is issued. After checking the contract refinement is correct, we are able to apply single top-down tightening on the parent contract speed_control of the system component in order to simplify the contract refinement. Suppose in this case we do not obtain any simplification and we proceed in a top-down fashion in the decomposition tree.

We consider now the RedundantSensor where two contracts are defined, sense and sensed_speed_is_present (see Fig. 2), which are refined by the contracts of the subcomponents of RedundantSensor. After the contracts specification and refinement is completed at this level and the check of the correctness of the contract refinements for sense and sensed_speed_is_present of component BSCU is successful, we proceed with tightening these contracts.

We observe that these contracts share many subcontracts, and in particular they share the subcontract of the Selector component, which as explained in Sect. 3.2 can be simplified. In this situation, independently from the order of tightening the contracts sense and sensed_speed_is_present, the result of each one can in principle break the correctness of the other and vice versa. This is due to the fact that the tightening technique proposed in the previous section only ensures the correctness of an individual contract refinement. We call this the problem of sharing contracts.

We propose a solution where we ensure that, when we are tightening a contract refinement, the correctness of the parallel refinements (those that share some subcontracts with the current one) is preserved. We called this solution parallel tightening which is presented in Sect. 5.2.

Fig. 2
figure 2

Sharing of contracts between sense and sensed_speed_is_present in component BSCU

Although parallel tightening produces a result that is correct also in the case of shared subcontracts, ensuring that all refinements are correct may mean that no simplification is possible. As an alternative approach, we can simply duplicate the shared contracts so that the refinements are independent and can be tightened as proposed in Sect. 4. This duplication approach is described in more detail in Sect. 5.3.

We would like to remark here that we are not proposing an automatic application of tightening, simplifying all contract refinements of a system architecture iteratively until reaching a fixpoint. We think that from the designer side, it will be really difficult to understand such tightened results at once. Thus, we propose to analyze one tightening result at a time, as explained at the beginning of the section.

5.2 Parallel tightening of a contract refinement

We define formally the problem of parallel tightening of a contract refinement as follows. Given a contract C, a set of contracts Sub such that \(Sub \preceq C\), and a set of contract refinements \(Sub_k \preceq CR_k\) such that for every k, \(1\le k\le n\), \(Sub_k \cap Sub \ne \emptyset \), a parallel tightening of this contract refinement is given by a contract \(C'\), a subset \(\underline{Sub}\) of Sub, and a set of contracts \(\underline{Sub}' = \{C'_S~|~ C_S\in \underline{Sub}\}\) such that:

  • \(\underline{Sub}' \preceq C'\)

  • \(C'\preceq C\) and, for every \(C_S\in \underline{Sub}\), \(C_S \preceq C'_S\).

  • \(Sub_k' \preceq CR_k\), for every k, \(1\le k\le n\)

where \(Sub'_k=\{C_S \mid C_S\in Sub_k\setminus \underline{Sub}\}\cup \{C'_S \mid C_S\in Sub_k\cap \underline{{Sub}}\}\) (i.e., if the subcontract is not shared, it remains the same; otherwise, it is simplified).

In order to deal with a parallel contract refinement, we have to generate not only the proof obligations of the given contract refinement \(Sub \preceq C\) but also the proof obligations of the related contract refinements whose have subcontracts in common. Suppose we want to obtain a parallel top-down tightening of \(Sub \preceq C\), we add steps 3 and 4 to the original procedure as follows:

  1. 1.

    We transform C and \({Sub}\) into parametrized versions \(C^P\) and \({Sub}^P=\{C_S^P\mid C_S\in {Sub}\}\) with parameters P such that for every evaluation \(\gamma \) of P, if \(\gamma ({Sub}^P)\preceq \gamma (C^P)\), then \(\langle {\gamma (C^P),\gamma ({Sub}^P)}\rangle \) is a top-down tightening of \(\langle {C,{Sub}}\rangle \).

  2. 2.

    We extend the construction of the proof obligations of \(\gamma ({Sub}^P)\preceq \gamma (C^P)\) by injecting further parameters \(P_{Sub}=\{p_{C_S}|C_S\in {Sub}\}\), one for each subcontract \(C_S\in {Sub}\), such that for every evaluation \(\gamma \) of \(P\cup P_{Sub}\), \(\gamma (\underline{{Sub}}^P)\preceq \gamma (C^P)\) where \(\underline{{Sub}}^P_\gamma =\{C_S^P\mid \gamma (p_{C_S})=\top \}\).

  3. 3.

    We search for those contract refinements \(Sub_k \preceq CR_k\) such that \(Sub_k \cap Sub \ne \emptyset \).

  4. 4.

    We generate the proof obligations for each of these related contract refinements \(Sub_k \preceq CR_k\) considering the parametric version of the subcontracts in common, and we add them to the set PO.

  5. 5.

    We solve the multiple parameter synthesis problem by either the compositional approach or encoding all POs into a single one.

These new steps are sketched in Algorithm 5.

figure e

5.3 Removing the sharing of contracts by duplication

In order to deal with the problem stated above regarding some situations when parallel tightening does not produce good results, we propose a solution by removing the sharing of contracts by duplication of contracts and running single tightening on the input contract refinement. Let us better explain this method using an example, by taking up again the contract refinements depicted in Fig. 2, where there are two contract refinements.

Fig. 3
figure 3

Duplication of contract switch in component Selector

Suppose now that we want to tighten the contract refinement (1) using parallel tightening. As a result we do not get any tightening, obtaining the same contract refinement. Then, we first duplicate the contracts that are shared by the two refinements. We update the contract refinements so that one uses the original subcontracts, while the other uses the duplicated ones. For example, we duplicate the contract switch of Selector and update the refinements as shown in Fig. 3. Finally, we are able to run the single tightening procedure on each refinement in isolation.

This alternative method can be also applied during the development process in a top-down fashion ensuring the preservation of the correctness of contract refinements for dealing with the problem of shared contracts. The order in which the contract refinements are selected for removing the sharing of contracts by duplication and then run single top-down tightening is arbitrary.

6 Experimental evaluation

6.1 Details of the implementation

We have implemented the algorithms described in the previous sections on top of OCRA [11], a tool for architectural design based on contract-based design. In more details, we implemented a new command in OCRA called ocra_tighten_contract_refinement that takes as input an OCRA specification, a contract’s name, a component’s name, and produces as output a number of OCRA specifications containing the tightened versions of the given contract and its subcontracts. The command has an option to allow the user to select the approach to the multiple synthesis problem (see Sect. 4.4), either the compositional approach (Sect. 4.4.1) or the encoding approach (Sect. 4.4.2). Other options in the command deal with the problem of sharing contracts: the user can choose to perform a parallel tightening or duplicate the contracts and run a single top-down tightening. Regarding the parameter synthesis algorithm, we have used as back-end an implementation reported in [7]. Since the synthesis is quite expensive for large number of parameters, we arbitrarily limit the injection to 350 parameters so that only the higher level of the formula structure is considered during weakening/strengthening (i.e., during the recursion of Algorithms 3 and 4, when reaching 350 parameters, the recursion is stopped and subfomulas are considered without modifications). This allows to get a tightening also in cases in which the definitions would produce many more parameters making the synthesis blow up.

We also implemented self checks to validate the results: first, we automatically check that each tightened contract refinement is correct; second, we automatically check for each tightened specification that the original formula entails the weakened formula.

6.2 Description of benchmarks

We have taken several benchmarks from case studies developed using OCRA in past projects. Some examples are: different versions of the Redundant Sensor described in Sect. 3.1, different variants of a Wheel Brake System [6, 15], and an Airbag system [1]. Particularly, an interesting case study is taken from [6], where the authors presented a complete formal analysis of the different architectures described in the AIR6110 [30], a document describing the informal design of a Wheel Brake System (WBS), covering all the phases of the process, and modeled the case study by means of a combination of formal methods including contract-based design using OCRA, model checking and safety analysis.

Overall, the benchmarks suite comprises 909 contract refinements, with on average 8.4 subcontracts. 593 out of these 909 share some contracts with other contract refinements.

6.3 Experimental results

6.3.1 Tightening a single contract refinement

We carried out an experimental evaluation on the 909 contract refinements comparing the contract specification before and after simplification with respect to the length of the formulas on the original contracts.Footnote 3 The results of applying single top-down tightening is shown in Fig. 4, where blue crosses represent those cases which all subcontracts are relevant after tightening and red circles indicates those cases in which the simplification involves the removal of some irrelevant subcontracts. From the results, we can clearly see a significant simplification. 268 out of the 909 contract refinements did not get any simplification, which seems a reasonable result.

Fig. 4
figure 4

Analysis of length of formulas for single top-down tightening

Fig. 5
figure 5

Parameter scalability for single top-down tightening

Fig. 6
figure 6

Comparison between compositional and encode approach for single top-down tightening

In Fig. 5, it is shown how our approach scales with respect to the number of parameters used for tightening a contract refinement and the time for computing the parameter region for three extended versions of the WBS example. In Fig. 6 we compare the performance for computing the parameter region using the encoding and compositional approaches for single top-down tightening. We can see that the compositional approach is much more efficient than the encoding one. This is probably due to the fact that many assumptions are true (on average 5.04) so that the corresponding proof obligations are trivial and do not restrict the resulting parameter region. In the compositional approach, this means that the computation time for these proof obligation is negligible. In the encoding approach, although the resulting region is the same, the problem is syntactically more complex.

Table 3 Results of irrelevant contracts for single top-down tightening

We also report on Table 3 the results of the average of the irrelevant subcontracts removed for each case study on single tightening (fourth column). We can observe that such number is higher for those benchmarks with a complex architecture. The reason is because the designer has to select manually the subcontracts which became a more difficult task with big architectures. Consequently, he/she may include subcontracts that are not relevant.

Fig. 7
figure 7

Analysis of length of formulas applying duplication and single top-down tightening

Fig. 8
figure 8

Comparison between compositional and encode approach for parallel top-down tightening

6.3.2 Taking into account shared contracts

For analyzing the problem of sharing of contracts we have considered the 593 contract refinements that contain shared contracts. We have run parallel tightening on these where we did not get any simplification in most of them. More precisely, we can simplify only 5 contract refinements. This is a result that can be expected due to we are including the proof obligations of other contract refinements that shared some contracts and it is not possible to get any simplification by keeping all contract refinements correct. Therefore, we have applied duplication of contracts and then run single tightening on those contract refinements that we did not get any simplification with parallel tightening and we have obtained a lot of improvement deployed in Fig. 7, where blue crosses represents those cases which all subcontracts are relevant after tightening and red circles indicates those cases in which the simplification involves the removal of some irrelevant subcontracts. Moreover, we remark that for only 31 contract refinements we did not get any simplification.

Finally, we show in Fig. 8 a comparison of the synthesis approaches for parallel top-down tightening. We can observe that in some cases the compositional approach is faster than the encoding one and vice versa. The performance of the encoding approach is therefore better than the single tightening case. This is probably due to the fact that in the parallel problem, there are more cases in which multiple proof obligations restrict significantly the parameter region. In fact, as said above, we did not get any simplification in general, which means that the intersection of the parameter regions obtained from the different proof obligation is empty. The encoding approach may take benefit by propagating constraints on the parameters given by one proof obligation to simplify other proof obligations in the monolithic encoding.

All benchmarks have been performed with a time limit of 10 min considering the checking of the contract refinement after tightening, the computation of the parameter region for the encoding and the compositional approach, and the check of the entailments properties. For the 909 contract refinements, 65 could not be completed applying single tightening within the timeout. We have run our experiments on a Linux machine with 8 CPU of 3.40 Ghz Intel Xeon, with a memory of 15 Gb. The benchmarks and executables for reproducing the results are available at https://es.fbk.eu/people/demasi/TightenArch/experiments.html.

6.4 An example of applying tightening on a system architecture

In this subsection we report the evaluation of our tightening techniques on a case study where we have applied these step by step following the process described in Sect. 5 and manually inspecting the results at each step.

Fig. 9
figure 9

WBS arch4 (simplified version)

We take the most complex architecture, arch4, defined in [6] and we apply tightening on the contract refinements from the system component to the leaf components in a top-down fashion. A simplified version of the system architecture of arch4 Footnote 4 is depicted in Fig. 9. We will show the application of our technique only for a subset of contracts just for a reason of space. We start from the system component called wbs_arch4 which is decomposed into a physical system and a control system. We take one of its contracts never_inadvertent_braking_of_wheel_8 and we apply single tightening. As a result, we obtain 2 tightened variants of this contract refinement. While the new refinements are correct, other contract refinements are broken due to the fact that never_inadvertent_braking_of_wheel_8 shares some subcontracts with other contracts (e.g., never_inadvertent_braking_of_wheel_i for \(0 \le i \le 7\)) defined in the system component like the subcontract system_validity from control system component. Then, we run parallel tightening on this contract and we do not get any tightened variant. Therefore, we remove the sharing of contracts by duplication and we obtain two tightened variants as a result. We now take one of these variants and we continue in the decomposition of the contract considering the contract expected_behavior_brake_as_cmd_8 in the ControlSystem component which is just refined by the subcontract expected_behavior_brake_as_cmd_8 of the BSCU component. We run just a single tightening on this contract and we do not obtain any simplification.

We continue going into a top-down fashion and we would like to tighten the contract expected_behavior_brake_as_cmd_8 in the BSCU component which is decomposed into two redundant Channel, an OrGate, and a 12 SwitchGate components. This contract shares several subcontracts with other contracts (e.g., expected_behavior_brake_as_cmd_j for \(0 \le j \le 7\)) defined in the BSCU component. The shared subcontracts correspond to different subcomponents, e.g., system_validity, command_creation_alternate_k (\(1 \le k \le 4\)), and command_creation_normal_l (\(1 \le l \le 8\)) of the Channel component. Also, subcontract or_behavior of the OrGate component. We first try with parallel tightening but we do not get any simplification. Then, we remove the sharing of contracts by duplication and we obtain as result that several subcontracts are not needed for the contract refinement. In this case that the subcontracts command_creation_alternate_k (\(1 \le k \le 4\)) and command_creation_normal_l (\(1 \le l \le 8\)) are irrelevant. We have investigated the reason and we observe that the contract system_validity is too weak. Therefore, we manually strengthen the contract and we rerun the procedure of duplication and we observe that now two more contracts channel_1.command_creation_normal_8 and channel_2.command_creation_normal_8 for the two redundant Channel are relevant for this contract refinement.

We continue by tightening the contract command_creation_normal_8 from the Channel component which is refined by the subcontract command_creation_normal_8 of CommandSystem component. This contract from the Channel component does not share contracts. By applying tightening, we do not obtain any simplification.

We continue in a top-down fashion where we observe that the Channel component is decomposed into component MonitorSystem and CommandSystem, where the first one is a leaf component which means that we have covered one branch of decomposition and the refinements of contracts from the system component. Subsequently, we tighten the contract command_creation_normal_8 of CommandSystem component. This component is decomposed in four WheelPairCommandSystem components. The contract command_creation_normal_8 is refined by contract command_creation of WheelPairCommandSystem component. This is shared by contract command_creation_normal_4 of CommandSystem. Thus, we run parallel tightening on the contract and we do not get any tightened variant. Then, we try by removing the sharing by duplication and we get a simplified version of the contract refinement command_creation_normal_8. Finally, as CommandSystem is a leaf component we have covered another branch of decomposition and the contracts refinement from the system component.

We conclude that the tightening techniques can support the designer during the process for the contracts specification and refinement and also when the specification of the architecture is already complete like in this case.

6.5 Discussion of the results

As shown by the results in Fig. 4, contract tightening is very effective and can simplify some formulas aggressively. This may be found surprising, especially for models coming from a solid benchmark, like the one presented in [6]. Investigating further the reason why such simplifications are possible, we discovered that in some cases there are indeed shared contracts. We evaluated these cases using the parallel tightening technique to see when the simplifications could be maintained keeping correct all refinements in the specification and the result showed that in the case of [6] none of such simplifications was possible. This means that in order to apply the tightening we have to first separate the refinements by either duplicating or removing the shared subcontract. We have applied on these cases the procedure for removing the sharing of contracts by duplication and we have obtained significant simplifications. This strengthens the usability of tightening: the designer typically focuses on having the contract refinement correct and reuse as much as possible existing contracts without caring about the burden of the engine due to the additional redundant contracts. In a second phase, tightening can be run to eliminate such redundancy in a fully automatic way. On the other hand, we have observed in some situations that redundancy is introduced by the designer on the functionality of the components in order to deal with failures of the system. In general, the tightening techniques cannot detect this kind of redundancy and consequently it is removed, so the designer after inspecting the result of tightening has to decide to keep the redundancy or not in this case.

We remark that the tightening technique assists/supports designers on the specification of the contracts and refinement during the development process by simplifying the contract refinements at the level of formulas and contracts. The designers are responsible of analyzing the tightening results and decide for example which one of them is taken for continue with the application of the approach in a top-down fashion. Moreover, duplication of contracts could produce complicated specifications if all duplicates are considered and not significant simplification is obtained. Then, after applying single tightening the designer should analyze the results and taking into account the sharing of contracts, he/she has to decide to keep the original refinement or the result obtained on the duplication of the contracts.

7 Related work

This article is a revised and expanded version of a conference paper presented at SEFM 2016 [10] and it extends it as follows: (1) it describes how the tightening techniques can support in general the development process in a top-down fashion for the contracts specification and refinement of a system architecture; (2) it extends the formal definition of tightening to provide the designer with information regarding which contracts are sufficient for (or prevent) correct refinement; (3) it introduces the problem of sharing contracts and provides a solution for this problem called parallel tightening, moreover, proposes an automatic approach for removing the sharing of contracts by duplication; (4) it presents a new approach for the multiple validity parameter synthesis problem called compositional approach; and (5) it proposes a more thorough experimental analysis, including more insights on the results of single tightening, an evaluation taking into account shared contracts, and a comparison of the two synthesis approaches.

We are not aware of similar works in the context of contract-based design. The problem of contract tightening is related to vacuity checking [24], unsatisfiability core extraction [12]. Unsatisfiability cores are used also to find which portions of a system model are relevant in an inductive proof [21]. Unsatisfiability cores are typically restricted to conjunctions of formulas. The probably most related work is the extension of the notion of unsatisfiability core to temporal formulas expressed in LTL proposed in [31]. However, the design problem, the formal problem, and the technical solution are very different. First, differently from the above-mentioned problems, we are not weakening/strengthening the occurrence of a subformula, but we need to weaken/strengthen all occurrences of an assumption/guarantee inside the proof obligations in the same way. Second, we do not have just one property to simplify, but every assumption/guarantee that is simplified occurs in different proof obligations; this corresponds to different unsatisfiability or model checking problems to consider at the same time. Third, we reduce the problem to a parameter synthesis problem and we ensure the monotonicity of parameters to ensure scalable results.

Also the work described in [23] addresses the problem of simplifying a contract refinement, but with a different purpose and solution: the approach relies on a library of contracts and refinement relations considered as additional inputs to the refinement check problem, and simplifies the contract refinement based on such library. The main objective of the authors is to improve the performance of the refinement check based on the library, while we search for a tighter version of the contracts that still ensure the correctness of the refinement.

8 Conclusions and future work

We have presented in this article a technique to support the development process for the contracts specification and refinement of a system architecture by simplifying the contract refinements along this process. Motivated by the need of validating contract-based designs, we have defined the problem of tightening a contract refinement. This consists of automatically synthesizing simplified versions of the involved assumptions and guarantees, including removing those that are irrelevant in the contract refinement. We consider also the tightening problem within a system architecture that contains multiple contract refinements that share some subcontracts. Our tightening techniques are based on the synthesis of parameters of temporal satisfiability problems. We have evaluated the approach on a number of benchmarks and showed that the solution is effective and scalable. Moreover, we have also manually inspected the results on a significant case study obtaining interesting feedback.

In future research, we plan to extend the approach to consider also the tightening of metric operators and the preservation of realizability. We also plan to detect and avoid the removal of redundancy introduced by the designer for specific goals like incorporating redundancy by adding additional components in order to deal with failures of the system.