• No results found

Basic Common Subexpression Elimination (CSE)

Common subexpression elimination (CSE) is a well-established optimisation technique originating from code optimisation [20]. In program code, an expressionE is called com- mon, ifE has been previously computed and all variables inEhave not changed since the previous computation.E can be eliminated [20] by reusing the previously computed value (and register) and discardingE.

Unlike in code optimisation, in CP we are dealing with expressions that representrelations

and not consecutive instructions. Therefore, we distinguish two kinds of common subex- pressions: first, we call two expression treesE1 andE2 identical, if they are the same wrt

their syntax. Second, we call E1 and E2 equivalent if they are semantically equivalent

under all satisfying assignments. In other words, identical subexpressions are common with respect to their syntax, equivalent subexpressions are common with respect to their semantics. For example, the two expressions a∗(b +c) and (b +c)∗a are not identi- cal, but equivalent. However, their subexpressions (b +c) and (b+c)are identical and

equivalent. Eliminating all equivalent common subexpressions is hard, therefore, we re- strict our CSE-algorithm to eliminating allidenticalsubexpressions. However, we will see that some families of equivalent common subexpressions can be easilyreducedto identical subexpressions.

Expressions in CP are similar to expressions in related areas like Proof Theory, SAT or Model Checking, where CSE is a wide-spread technique [60, 55, 49]. In most of those disciplines, CSE is performed by transforming expression trees into acyclic directed graphs (DAG) where common nodes are merged [71]. This approach has also been applied to Numerical CSPs [6]. However, big finite-domain constraint instances can contain 10,000s of complex constraints, resulting in an extremely large DAG. Therefore, we introduce an alternative approach of CSE, that is embedded into the necessary task of flattening.

Embedding CSE into flattening provides three main benefits. First, it is easy to do. Second, extending a necessary task like flattening with some cheap operations that apply CSE, instead of performing an additional CSE-algorithmon topof tailoring, saves computational

Algorithm 4.1 FLATTEN INSTANCE CSE (MS) flattens constraint instance MS to MS!

with CSE. Differences/extensions to Algorithm 3.1 are given inred font.

Require: MS: problem instance

1: globalflatConstraints, constraintBuffer, auxVars←empty lists

2: globalhash-tableempty hash-table

3: for allEMS.constraintsdo

4: constraintBuffer empty 5: E! 0FLATTEN CSE(E,false) 6: ES! ←E0! ( # iEi!∈constraintBuffer) 7: flatConstraints.add(ES!) 8: MS!.constraints←flatConstraints 9: MS!.vars← {MS.vars∪auxVars} 10: return MS!

time and memory. Third, if an instance contains common subexpressions, we also save flattening time, since duplicate subexpressions are never flattened twice.

This section delivers the following contributions. First, we introduce an alternative, light- weight CSE approach that is embedded into flattening. Second, we show that for many instances, the proposed CSE-flattening approach lies in the same complexity class (wrt flattening time and memory) as standard flattening; additionally, we theoretically describe the potential benefit in instance reduction that can result from CSE (Sec. 4.2.1). In the following sections, we present techniques to increase the number of identical common subexpressions in an instance in order to further improve the instance (Sec. 4.3). Further- more, in Sec. 4.4, we discuss the scope of our CSE approach and show which CSs we do not eliminate (and why). Finally, a thorough empirical analysis can be found in Chapter 8.

4.2.1 Extending Flattening with CSE

In the previous chapter we have seen a typical flattening procedure (Sec. 3.3), FLAT- TEN INSTANCE , that invokes FLATTEN on every constraint expression of the instance. Note, that if two (or more) constraints have identical subtrees (common subexpressions) then FLATTENwillnotexploit this equivalence, which results in three redundancies:

1. FLATTENcreates a different auxiliary variable for each subtree, while each identical subtree could/should be represented by the same auxiliary variable.

2. Creating redundant auxiliary variables also creates redundant constraints to initialise these variables.

3. FLATTENisrepeatingwork that it has already performed.

Consequently, extending FLATTENto detect and exploit common subexpressions so as to eliminate these redundancies is desirable.

The CSE-flattening Algorithm

Flattening can be easily extended with CSE: first, a hash-table is introduced to map ev- ery flattened subexpression to the corresponding auxiliary variable. Second, every time a subexpression E is about to be flattened, the hash-table is consulted: if E was flattened before, then the hash-table returns the corresponding auxiliary variable and E does not have to be flattened. Otherwise, E is flattened to an auxiliary variable aux and an entry E −→ auxis added to the hash-table. The main advantage of using a hash-table for this process is that all operations involving adding or retrieving data from the hash-table are constant on average [46].

We formalise CSE-based flattening in the algorithm FLATTEN INSTANCE CSE(Alg. 4.1), an extension of FLATTEN INSTANCE (Alg. 3.2). The hash-table maps every flattened

Algorithm 4.2 FLATTEN CSE (E,flatten2Aux), recursive procedure based on FLATTEN (Alg. 3.2), extended with ahash-table, mapping all flattened subexpressions to the corre- sponding auxiliary variable. Extensions are given inred font.

Require: E: expression tree,flatten2Aux: Boolean flattened to aux var

1: if¬(all ofE’s children are leaves)then

2: for allei∈children(E)do 3: if ¬(ei.isLeaf)then

4: Stringei ←toString(ei)

5: if hash-table.contains(Stringei) then

6: auxhash-table.get(Stringei)

7: else

8: aux FLATTEN CSE(ei,S,true) 9: hash-table.add(Stringei,aux)

10: E.replaceChildWith(ei,aux) 11: ifflatten2Aux then

12: AuxcreateNewVariable(E.lb,E.ub);auxVars.add(Aux)

13: constraintBuffer.add(‘Aux=E’)

14: return Aux

15: else

16: return E

subtree to its corresponding auxiliary variable and is used by the new recursive flattening procedure, FLATTEN CSE (Alg. 4.2), based on FLATTEN, to detect identical subexpres- sions that have been previously flattened:

FLATTEN CSE:

1. whenever a non-leaf childeiof current nodeEis flattened, we look for an entry ofei

inhash-table(line 5). Note that all subexpressions are stored in String format, hence ei has to be converted into String format (line 4) before the hash-table is consulted.

2. If there is an entry, we re-use the auxiliary variable to whichei is mapped (line 6),

instead of flatteningei again.

3. Otherwise (i.e. if there is no match inhash-table), we flattenei to auxiliary variable

e!

i (line 8) and addei →e!i tohash-table(line 9).

Clearly, FLATTEN INSTANCE CSEwill flatten eachuniquesubnode exactly once.

Lemma 4.2.1. If constraint instance MS contains m constraints that contain n subex-

pressions of which nu are unique (with n≥nu≥m), then FLATTEN INSTANCE CSE will

generate a flat instanceMS! withnu−mauxiliary variables andnuconstraints.

Proof. FLATTEN INSTANCE CSE applies FLATTEN CSE to every constraint E in MS

(line 5 in Alg. 4.1). If E has a non-leaf child ei (a subnode), then there are two cases

(line 5 in Alg. 4.2): first, if there is no entry ofeiinhash-table,ei is flattened to auxiliary

MS, there will be an entry inhash-table. Second, if there is an entry of ei in hash-table,

(ei must have been flattened before), the corresponding auxiliary variableauxis retrieved

fromhash-table(line 6). Therefore, FLATTEN CSEis only invoked on those children that

have not been previously flattened, i.e. every unique node is flattened exactly once. This results in one auxiliary variable and one ‘Aux=E’-constraint for every unique node that is not a root node, and one constraint for every unique root node (see Lemma 3.3.1). There- fore, M!

S containsnu −m auxiliary variables (one for each unique node, minus the root

nodes) andnu constraints (nu−mAux=E’-constraints andmroot-node-constraints). Comparing CSE-flattening with Standard Flattening

We analyse the differences of applying FLATTEN INSTANCEand FLATTEN INSTANCE CSE on problem instanceMSwithmconstraints andnsubexpressions of whichnuare unique,

We denoteM! the flat instance generated by FLATTEN INSTANCE and M!

CSE the flat in-

stance generated by FLATTEN INSTANCE CSE. The maximal number of subexpressions in any expression in instanceMS is denotedˆk, and the longest String representation of any

subexpression inMS is denoted s. We consider theˆ worst case with respect to the trans-

lated instance, in which the instance contains no common subexpressions, i.e. the number of unique subexpressions equals the number of subexpressions,nu =n.

We assume that the hash-table is implemented in such a way that the cost of each lookup is independent of the number of elements stored and operations to add and retrieve entries to the hash-table are performed in constant time. Note that this assumption holds for most standard implementations of hash-tables, e.g. in Java 1.5.0 [78] which we used in our implementation.

Theorem 4.2.1. MCSE! containsn−nufewerconstraints and auxiliary variables thanM!.

Proof. M! containsnconstraints andn−mauxiliary variables (Lemma 3.3.1), andMCSE!

containsnuconstraints andnu−mauxiliary variables (Lemma 4.2.1). Sincen≥nu,MCSE!

containsnnu fewer constraints and auxiliary variables thanM!.

Theorem 4.2.2. Thespace complexityof FLATTEN INSTANCE CSEisOsn), wheresˆis

the maximal String length of any subexpression in instanceMS.

Proof. From Theorem 3.3.2 we know that the space complexity of FLATTEN INSTANCE

lies inO(n). FLATTEN INSTANCE CSEuses the same data structures as FLATTEN INSTANCE

, with the addition of the hash-table to store flattened subexpressions. The hash-table is String-based, i.e. each expression tree and auxiliary variable is stored as a String (instead of as a tree), which facilitates matching. It storesnu unique nodes and the corresponding

auxiliary variables, thus the hash-table uses 2 nu ∗sˆunits of memory, where sˆis the

maximal String length of a subexpression in MS. Therefore, FLATTEN INSTANCE CSE

uses2sˆnu∗more units of memory than FLATTEN INSTANCE. Sincenu≤n, the space

Theorem 4.2.3. Thetime complexity FLATTEN INSTANCE CSE isO((ˆk + ˆs)n), where ˆ

k is the maximal number of subexpressions in any expression in instanceMS andˆsis the

maximal String length of a subexpression in instanceMS.

Proof. From Theorem 3.3.1, we know that FLATTEN INSTANCE lies in O(n). FLAT-

TEN INSTANCE CSEadds instructions to the flattening process of FLATTEN INSTANCE : First, the subexpression is converted to String format, an operation that is in O(k)where

k is the number of subexpressions the to-be-flattened expressionE contains. In the worst case, this is performed for all nsubexpressions, yielding a runtime of Ok∗n), wherekˆ is the maximal number of subexpressions an expression contains. Second, the hash-table check followed by either retrieving an object from the hash-table (if the check is posi- tive) or creating an entry to the hash-table (if the check is negative). Since operations on hash-tables are in O(1) on average they are all together in O(1) since 2O(1) = O(1).

However, the hash-table operations require the String (representing the subtree) to be read, which lies inOs), whereˆsis the maximal String length of a subexpression in instanceMS.

From Lemma 4.2.1 we know that FLATTEN INSTANCE CSEflattens allnuunique nodes/-

subexpression exactly once, thus the hash-table operations lie inOsnu). In summary, the

runtime complexity of the CSE-operations (in addition to flattening) is Okn) +Osn),

since nu=n if the instance contains no common subexpressions and f is a constant. In

summary, this results in an overall runtime ofO(n) +Osn) +Okn) =O((ˆk+ ˆs)n).

Conclusion Standard flattening and CSE-flattening differ with respect to the factorssˆfor

space andˆkandˆsfor time complexity, whereˆsdenotes the longest String representation of

any subexpression inMS andkˆdenotes the maximal number of subexpressions in any ex-

pression inMS. Note, thatsˆandˆkare often independent ofn(and can hence be considered

constants), since parameters often only scale the number of constraints, but not the width or depth of the expression trees. As an example, consider again the Graph Colouring Problem (Fig. 2.2 in Section 2.1), where the number of constraints increases with the number of vertices and colours, but the width and depth of the corresponding expression trees stays the same. Therefore, for many problem classes, the time complexity of flattening with or without CSE lies in the same complexity class, since Okn) = O(n) if ˆk is a constant. Furthermore, in cases whereˆkis not a constant, note thatˆkis always strictly smaller than

n with the exception of the special case, where the instance contains only one constraint, wherekˆ=n.

Common subexpression elimination as described in this section has been implemented in the tool TAILOR(Sec. 3.1.1) and we have studied the effects of CSE on various problem classes in an empirical analysis that is further described in Sec. 8.2. In this empirical study we observe that in practice, the difference between both flattening approaches is negli- gible, and, if the instance contains common subexpressions, CSE-flattening often clearly outperforms standard flattening in both runtime and memory.