• No results found

A Simplex Version for SMT

2.7 Standard Arithmetic Decision Procedures for SMT

2.7.1 A Simplex Version for SMT

Most SMT solvers implement the following version of the dual simplex al- gorithm as their linear rational arithmetic theory solver [9, 41, 42, 50, 57]. This specific version of the dual simplex algorithm was first presented by Dutertre and de Moura [58]12. Whenever we refer to the simplex algorithm in the remainder of this thesis, we refer to this specific version.

It has been proven that almost every variation of the simplex algorithm has an exponential worst-case runtime complexity [129].13 For the classical simplex algorithm as presented by Dantzig [48], this worst case occurs for the KleeMinty cube [99], i.e., a unit hypercube of variable dimension whose corners have been perturbed. Since there are polynomial decision procedures for linear rational arithmetic [94, 96], this also means that there are at least theoretically faster decision procedures. But in contrast to those polynomial decision procedures, the simplex algorithm has all properties necessary for a good theory solver: it produces minimal conflict explanations, handles back- tracking efficiently, and is highly incremental. In practice, these properties are very important to the overall performance of an SMT solver [62]. Due to these properties and the fact that the simplex algorithm rarely reaches its worst case in practice, we prefer the simplex algorithm over theoretically faster decision procedures.

The input of the simplex algorithm (Figure 2.2) is in tableau represen- tation (Section 2.4.2). Therefore, the input consists of two sets of variables z1, . . . , zn ∈ N and y1, . . . , ym ∈ B, the two bound value functions L and U, a tableau Az = y, and a set of bounds L(xj) ≤ xj ≤ U(xj) for the va- riables xj ∈ N ∪ B. Each row in this tableau represents one basic variable yi∈ B: yi= aTi z. This means that the non-basic variables z = (z1, . . . , zn)T define the basic variables y = (y1, . . . , ym)T over the tableau Az = y. The

12

The first version of the dual simplex algorithm is much older [48].

13

It is assumed that all versions of the simplex algorithm have an exponential worst-case runtime complexity because the simplex algorithm is NP-mighty [54].

Algorithm 1:pivot(yi, zj)

Input :A basic variable yi and a non-basic variable zj so that aij is

non-zero

Effect :Transforms the tableau so that yi becomes non-basic and zj

basic

/* Let yi= aTi z be the row defining the basic variable yi. We

rewrite this row as zj= a1

ijyi− P k6=j aik aijzk so it defines zj instead */ 1 A0 ∈ Qm×n; 2 y0i:= zj; 3 foryk∈ B \ {yi} do yk0 := yk; 4 zj0 := yi; 5 forzk ∈ N \ {zj} do z0k:= zk; 6 a0ij := 1 aij; 7 forzk ∈ N \ {zj} do a0ik:=− aik aij;

/* Then we substitute zj in all other rows with 1 aijyi− P k∈N \{zj} aik aijzk */ 8 foryl∈ B do 9 forzk ∈ N \ {zj} do alk0 := alk+ alja0ik; 10 a0lj := alja0ij; 11 end 12 N := {z10, . . . , zn0}; B := {y01, . . . , ym0 }; (Az = y) := (A0z0 = y0); Algorithm 2:update(zj, v)

Input :A non-basic variable zj and a value v∈ Qδ

Effect :Sets the value β(zj) of zj to v and updates the values of all

basic variables

1 foryi∈ B do β(yi) := β(yi) + aij(v− β(zj)); 2 β(zj) := v;

Algorithm 3:pivotAndUpdate(yi, zj, v)

Input :A basic variable yi, a non-basic variable zj, and a value v∈ Qδ

Effect :Pivots variables yi and zj and updates the value β(yi) of yito v

1 θ := v−β(ya i)

ij ;

2 β(yi) := v; β(zj) := β(zj) + θ;

3 foryk∈ B \ {yi} do β(yk) := β(yk) + akjθ;

4 pivot(yi, zj);

Figure 2.1: The pivot and update functions [58].

simplex algorithm exchanges variables from yi ∈ B and zj ∈ N with the pivotalgorithm (Figure 2.1). To do so, we also have to change the tableau via substitution. All tableaux constructed in this way are equivalent to the original tableau Az = y.

Algorithm 4:Check()

Output :Returns true iff there exists a rationally satisfiable assignment for the tableau and the bounds u and l; otherwise, it returns (false,yi), where yi is the conflicting basic variable

1 whiletrue do

2 select a basic variable yi such that β(yi) <L(yi) or β(yi) >U(yi)

3 if there is no suchyi then return true;

4 if β(yi) <L(yi) then

5 select a non-basic variable zj such that

6 (aij > 0 and β(zj) <U(zj)) or (aij< 0 and β(zj) >L(zj))

7 if there is no such zj then return(false,yi) ;

8 pivotAndUpdate(yi, zj,L(yi)) 9 end

10 if β(yi) >U(yi) then

11 select a non-basic variable zj such that

12 (aij < 0 and β(zj) <U(zj)) or (aij> 0 and β(zj) >L(zj))

13 if there is no such zj then return(false,yi) ;

14 pivotAndUpdate(yi, zj,U(yi))

15 end 16 end

Figure 2.2: The main function of the simplex algorithm [58]

The goal of the simplex algorithm is to find an assignment β that maps every variable xi to a value β(xi)∈ Qδ that satisfies our inequality system, i.e., A(β(z)) = β(y) andL(xi) ≤ β(xi) ≤ U(xi) for every variable xi. The algorithm starts with an assignment β that fulfills A(β(z)) = β(y) and L(zj)≤ β(zj)≤ U(zj) for every non-basic variable zj ∈ N . Initially, we get such an assignment through our tableau. We simply choose a valueL(zj)≤ β(zj) ≤ U(zj) for every non-basic variable zj ∈ N and define the value of every basic variable yi∈ B over the tableau: β(yi) :=Pzj∈Naijβ(zj). Note

that this only works if we assume that L(xj) ≤ U(xj) for every variable xj ∈ N ∪ B, which is easy to guarantee through a small preprocessing step sinceL(xj) >U(xj) already implies that the problem is unsatisfiable. As an invariant, the simplex algorithm continues to fulfill A(β(z)) = β(y) and L(zj) ≤ β(zj) ≤ U(zj) for every non-basic variable zj ∈ N and every intermediate assignment β.

The simplex algorithm finds a rationally satisfiable assignment or an explanation of rational unsatisfiability through the Check() algorithm (Fi- gure 2.2). Since all non-basic variables fulfill their bounds and the tableau guarantees that Az = y, Check() only looks for a basic variable that violates one of its bounds. If all basic variables yi satisfy their bounds, then β is a rationally satisfiable assignment and Check() returns true. If Check() finds a basic variable yi that violates one of its bounds, then it looks for a non-basic

Algorithm 5:CheckConflict(yi)

Input :A conflicting basic variable yi, i.e., a variable yi that was

returned by Check() in a pair (false, yi).

Output : Returns a minimal set of bounds that cause a conflict with the tableau Az = y

1 if β(yi) <L(yi) then

2 C :={yi≥ L(yi)} 3 forzj ∈ N do 4 if aij > 0 then C := C∪ {zj ≤ U(zj)}; 5 if aij < 0 then C := C∪ {zj ≥ L(zj)}; 6 end 7 end

8 if β(yi) >U(yi) then 9 C :={yi≤ U(yi)} 10 forzj ∈ N do 11 if aij < 0 then C := C∪ {zj ≤ U(zj)}; 12 if aij > 0 then C := C∪ {zj ≥ L(zj)}; 13 end 14 end 15 returnC

Figure 2.3: A conflict extraction function for the simplex algorithm [58]

variable zj fulfilling the conditions in lines 6 or 12 of Check(). If it finds a non-basic variable zj fulfilling the conditions, then we pivot yi with zj and update our β assignment so β(yi) is set to the previously violated bound value, which satisfies our invariant once more. If it finds no non-basic vari- able fulfilling the conditions, then the row of yi and all non-basic variables zj with aij 6= 0 build an unresolvable conflict. Hence, Check() has found a row that explains the conflict and it can return unsatisfiable.

The simplex algorithm terminates when it uses an appropriate selection strategy, e.g, Bland’s rule [25]. Bland’s rule is based on a predetermined va- riable order and always selects the smallest variables fulfilling the conditions for pivoting according to a predetermined variable order.

This already concludes our general description of the simplex algorithm. However, we are still missing the properties that turn the simplex algorithm into a well-suited SMT theory solver, i.e., minimal conflict explanations, high incrementality, and efficient backtracking. We explain why the simplex algorithm fulfills these properties in the remainder of this subsection.

Conflict Extraction

In a typical SMT solver (for more details see Section 2.6), a SAT solver based on CDCL (conflict-driven clause-learning) selects and asserts a set of theory literals that satisfy the boolean model. Then the theory solver verifies that the asserted literals are consistently theory satisfiable. If the theory solver finds a conflict between the asserted literals, then it returns a conflict explanation. The SAT solver uses the conflict explanation to start a conflict analysis that determines a good point for back jumping so it can select a new set of theory literals. Naturally, a good conflict explanation greatly enhances the conflict analysis and, therefore, the remaining search.

The literals asserted in our simplex based theory solver are bounds for our variables.14 This means that a minimal conflict explanation is a subset C of the asserted variable bounds. In order to be conflicting and minimal, the subset C has to fulfill the following properties: (i) C is, together with the tableau Az = y, unsatisfiable over the rationals; and (ii) any strict subset C0 ⊂ C is, together with the tableau Az = y, satisfiable over the rationals. Both conditions are also independent of the pivots applied to Az = y because pivoting is an equivalence preserving transformation.

We can derive such a minimal conflict explanation based on the output of the Check() call. If the call to Check() exits in line 7 with (false, yi), then the conflict explanation is

Cl={yi ≥ L(yi)} ∪{zj ≤ U(zj) : zj ∈ N and aij > 0} ∪{zj ≥ L(zj) : zj ∈ N and aij < 0}.

If the call to Check() exits instead in line 13 with (false, yi), then the conflict explanation is

Cu ={yi ≥ U(yi)} ∪{zj ≥ L(zj) : zj ∈ N and aij > 0} ∪{zj ≤ U(zj) : zj ∈ N and aij < 0}.

We can compute these sets with the function CheckConflict(yi) (see Fi- gure 2.3).

The sets are conflicting because Cl∪{aTi z−yi ≤ 0} and Cr∪{yi−aTi z≤ 0} are minimal conflicts with regard to Farkas’ Lemma (Lemma 2.5.6). Note that aT

i z− yi ≤ 0 and yi− aTi z≤ 0 are the two inequalities that define the i-th row aT

i z = yi in the tableau Az = y. Incremental Extension and Backtracking

Most CDCL(T) solvers do not just check full boolean models for theory sa- tisfiability but also some of the intermediate partial models. This is called (weakened) early pruning [130]. If the checked model is not theory satisfia- ble, then the SMT solver uses the conflict (i) to either prove that the whole

14Actually, the literals we assert are full inequalities aT

ix ≤ bi. Due to slacking, the

left side of those constraints is abstracted to a slack variable s such that s = aTix. The

definition of the slack variable s = aTix is directly stored in the simplex tableau and only a

Algorithm 6:AssertLower(xi, li)

Input :A variable and a new lower bound value to assert.

Output : Returns false if the new lower bound for xi violates xi’s current

upper bound. Returns true if the current assignment β satisfies the new bound. Otherwise, returns unknown.

Effect :Adds the current lower bound to the old bound sequence and setsL to li ifL(xi) < li≤ U(xi). If this violates the simplex

invariants, then updates β accordingly. 1 if U(xi) < li then return false;

2 if L(xi) < li then

3 O := [[O, (xi≥ L(xi), d)]]

4 L(xi) := li 5 end

6 if li ≤ β(xi) then return true;

7 if xi ∈ N then update(xi, li);

8 return unknown

Algorithm 7:AssertUpper(xi, ui)

Input :A variable and a new upper bound value to assert.

Output :Returns false if the new upper bound for xi violates xi’s current

lower bound. Returns true if the current assignment β satisfies the new bound. Otherwise, returns unknown.

Effect :Adds the current upper bound to the old bound sequence and setsU to ui ifL(xi)≤ ui<U(xi). If this violates the simplex

invariants, then updates β accordingly. 1 if L(xi) > ui then return false;

2 if U(xi) > ui then

3 O := [[O, (xi≤ U(xi), d)]] 4 U(xi) := ui

5 end

6 if ui ≥ β(xi) then return true;

7 if xi ∈ N then update(xi, ui);

8 return unknown

Figure 2.4: Incremental assert functions for the simplex algorithm [58]

problem was unsatisfiable or (ii) to find a prefix of the model that is still able to satisfy the conflict. This means that subsequent theory problems are connected as follows: either the problems are incrementally connected , i.e., (Ax ≤ b) ∪ (Dx ≤ c) follows (Ax ≤ b), or the problems are decrementally connected, i.e., (Ax≤ b) follows (Ax ≤ b) ∪ (Dx ≤ c).

Based on this, we call the runtime advantage an algorithm gains from having already solved a subset of the problem its incremental efficiency. Since most problems sent to an SMT theory solver are incrementally con- nected, its incremental efficiency is a major factor in determining its total efficiency. Similarly, we want to reduce the overhead that our theory solver

encounters when we have to backtrack, i.e., remove some of the literals in reverse chronological order. The less overhead this causes the more efficient our theory solver can backtrack. The simplex algorithm is in fact able to both incrementally add literals efficiently and to backtrack efficiently.

To this end, we first have to extend the simplex state to include more than just two sets of variables N and B, the two bound value functions L andU, the tableau Az = y, and the current assignment β. We additionally include a sequence of old bounds O, the current backtrack level d, and a backup assignment ω. Initially, O is empty, the current backtrack level d is zero, the bound value functions L and U map all variables to −∞ and ∞, respectively, the backup assignment ω and the current assignment β assign all variables to zero, and the initial tableau Az = y has a slacked row aT

i z = yi for all inequalities aTi z ≤ bi that appear in the SMT input problem. The latter is necessary so we never have to extend the tableau.

We extend our bound value functions—and, thereby, the set of literals we consider—with the functions AssertLower() and AssertUpper() (Fi- gure 2.4). Both functions have three possible return values: false, true, and unknown. They return false if the bound we want to add contradicts anot- her bound of the same variable, e.g., we already asserted xi ≤ 5 and are now asserting xi ≥ 6. This is necessary to guarantee our initial assump- tion that L(xi) ≤ U(xi) for all variables xi ∈ N ∪ B. They return true if the new bound value is already satisfied by our current assignment β. And they return unknown otherwise. The newly asserted bound replaces the current bound value if it is stricter, e.g., if we assert xi ≤ ui, then xi ≤ ui is stricter than xi ≤ U(xi) whenever ui < U(xi). If we replace a bound value, then we also append the replaced bound to the end of the sequenceO together with the current backtrack level. This is necessary for our efficient backtracking algorithm. Both functions also have to maintain the invariant L(zj) ≤ β(zj) ≤ U(xi) for every variable zj ∈ N . This means that we call update(zj, v) whenever the new bound value v violates a non-basic variable zj ∈ N .

Let us now look at a series of assertions that extend our current sequence of literals.15 First of all, we can assume without loss of generality that we start such a series of assertions from a simplex state with a rationally satisfiable assignment for the current bounds. We can stop our assertions as soon as any of the assertions in the series return false. In this case, we have found a conflict (i.e., the set consisting of the two contradicting bounds) and our model can no longer be satisfiable over the rationals. If all of our assertions return instead true, then our old assignment also satisfies all new bounds. Therefore, the extended model is still satisfiable over the rationals.

15

Algorithm 8:AddBTPoint()

Output : The new backtrack level.

Effect :If the current assignment is unsatisfiable, does nothing.

Otherwise, increments the backtrack level and creates a backup of the current assignment.

1 if β is not a satisfiable assignment then return d; 2 d := d + 1

3 ω := β 4 returnd

Algorithm 9:Backtrack(d0)

Input :The decision level to backtrack to.

Effect :Reverts the bound value functions so they map to the bounds upto decision level d0. Recovers a satisfiable assignment for the

decision level d0.

1 if d≤ d0 then return;

2 while O = [[O0, (γ, d∗)]] with d∗> d0 do 3 O := O0 4 if γ = (xi≥ li) then L(xi) := li; 5 if γ = (xi≤ ui) then U(xi) := ui; 6 end 7 β := ω 8 d := d0

Figure 2.5: A simplex backtrack function [58]

If the assertions return at least once unknown but never false, then we do not know whether the extended model is satisfiable or unsatisfiable over the rationals. However, all invariants for the simplex algorithm hold. Therefore, we can use Check() to determine the rational satisfiability of our model.

These assert functions are so efficient because they maintain the simplex invariants at the cost of a negligible overhead and because they never have to change the tableau. Moreover, any previous pivots to the tableau do not hurt the efficiency of the Check() function. In fact, the previous pivots prevent us from visiting assignments that we already eliminated as unsatisfiable. This means that Check() is more efficient if it previously solved a subset of the problem. Therefore, it is incrementally efficient.

Backtracking, i.e., removing bounds in reverse chronological order, can also be done efficiently with the simplex algorithm. However, we first have to define the points in our chronological order that we want to backtrack to. To this end, let us look again at the goal of a typical CDCL(T) solver.

A typical CDCL(T) solver wants to find a complete boolean model (i.e., sequence of literals) that is also theory satisfiable. Therefore, our goal is to find a sequence of bounds that is satisfiable over the rationals. Since an unsatisfiable sequence of bounds only becomes satisfiable by removing

bounds, this also means that we always want to backtrack to satisfiable pre- fixes of our bound sequence. An SMT solver can mark a subset of these sa- tisfiable prefixes as its backtrack points through the function AddBTPoint() (Figure 2.5).

Note that we always backtrack in a linear order. Therefore, the back- track points created by AddBTPoint() are actually backtrack levels d and we simply increment our current backtrack level whenever we need a new backtrack point. Apart from that, AddBTPoint() also makes a backup ω of the latest rationally satisfiable assignment. We need only one backup assignment for all backtrack points because ω satisfies all subsets of C if it satisfies C itself. This means that ω is a rationally satisfiable assignment for all previous backtrack levels.

Thanks to this backup assignment ω and our old bounds sequenceO, we are now able to efficiently backtrack to any previous backtrack level d0 with the function Backtrack(d0) (Figure 2.5). This function is efficient because it only reverts the bounds and exchanges the assignment.16