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-table←empty hash-table
3: for allE∈MS.constraintsdo
4: constraintBuffer ←empty 5: E! 0←FLATTEN 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: aux←hash-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: Aux←createNewVariable(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−m‘Aux=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!
containsn−nu fewer constraints and auxiliary variables thanM!.
Theorem 4.2.2. Thespace complexityof FLATTEN INSTANCE CSEisO(ˆsn), 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
uses2∗sˆ∗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 O(ˆk∗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 2∗O(1) = O(1).
However, the hash-table operations require the String (representing the subtree) to be read, which lies inO(ˆs), 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 inO(ˆsnu). In summary, the
runtime complexity of the CSE-operations (in addition to flattening) is O(ˆkn) +O(ˆsn),
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) +O(ˆsn) +O(ˆkn) =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 O(ˆkn) = 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.