3.3 Middle-End
3.3.1 Preprocessing
Preprocessing summarises all model transformations that are performed before the prob- lem model is flattened to a low-level representation. There are two main processing steps: model normalisation and type adaptions. Normalisation simplifies the problem instance and reduces equivalent but syntactically different representations to one unique represen- tation. Type adaptions are transformations of types (e.g. data structures) to conform to the target solver. This includes for instance flattening of multi-dimensional arrays to 1- dimensional arrays. These adaptions typical concern the whole problem instance and are therefore easiest performed during preprocessing, when the instance is still in a compact format: quantifications are not unrolled, so the model contains ‘less’ expressions.
Normalisation
The grammar of constraint expressions contains many equivalent representations, for in- stance, x+y+z andy+x+z where the equivalence stems from the commutativity of addition. A normal form without such equivalences provides many benefits. For instance, processing methods, like flattening, need only be implemented for one normalised sub- term and not for every other equivalent representation. Further benefits will be explained in Sec. 4.3, where instance optimisations are discussed. The core normalisation steps are evaluation and ordering of expressions, which are described in more detail below.
Expression Ordering We define an order ≤g over the expressions in ESSENCE!and
transform every expression tree into a minimal form with respect to this order. The general ordering rules are:
• Numbersare ordered according to their value
• Identifiersare ordered lexicographically
• Expressions separated by a comma are ordered according to the number of expres-
sion, i.e.
expression
<g expression, expression
<g expression, expression, expression
<g . . .
For instance,m[c1, c2]<g m[c1, c2, c3].
• We impose an order on terms composed by commutative operators opc ∈ { +, ∗, =, %=, ⇔, ∨, ∧ }: a term expression1 opc expression2 is ordered, if and only if expression1≤g expression2.
We summarise the expression order in Fig. 3.5. The topmost expression is weakest; if a non-terminal has several productions, the first production is the weakest. The order of operations is derived from the operator precedence.
Non-terminals (expressions) Ordering of possible productions Atom false <g true <g num <g ident <g ident [ArithmExpr]
<g ident [ArithmExpr, ArithmExpr]
<g ident [ArithmExpr, ArithmExpr, ArithmExpr]
<g . . .
Unary Operators -ArithmExpr <g !RelExpr
<g |ArithmExpr|
Binary Operators Expression=Expression <g Expression!=Expression <g Expression<Expression <g Expression<=Expression <g Expression>Expression <g Expression>=Expression <g Expression+Expression <g Expression-Expression <g Expression*Expression <g ArithmExpr/ArithmExpr <g ArithmExpr%ArithmExpr <g ArithmExprˆArithmExpr <g RelExpr<=>RelExpr <g RelExpr=>RelExpr <g RelExpr\/RelExpr <g RelExpr/\RelExpr Global Constraints alldifferent(ArrayExpr)
<g table(ArrayExpr,ConstArray)
<g element(ArrayExpr,Expression,Expression)
<g atmost(ArrayExpr,ArrayExpr,ConstArray)
<g atleast(ArrayExpr,ArrayExpr,ConstArray)
<g gcc(ArrayExpr,ConstArray,ArrayExpr) Quantified Expressions sumBindingexpression.ArithmExpr
<g forallBindingexpression.RelExpr
<g existsBindingexpression.RelExpr Binding Expression ident:SimpleDomain
<g ident,ident :SimpleDomain
<g ident,ident,ident:SimpleDomain
<g . . .
Figure 3.5: Expression Orderingin ESSENCE!
Expression Evaluation is important to simplify expressions involving constants and is
performed only to a certain extent to minimise the computational effort. Note, that ordering expressions simplifies evaluation: constant expressions are listed before decision variables, e.g.2 +x+ 5is ordered to2 + 5 +x, hence evaluation rules can be applied from left.
We apply constant evaluation (e.g. ‘2 + 5’ is evaluated to ‘7’), simple logical evaluation,
The table below summarises all transformations, excluding constant evaluation, which is straightforward. Note, that even though some expressions, like identity expressions, seem quite uncommon, they occur rather often at instance level, when parameters are replaced by constant values.
Algebraic Identities Algebraic Inverses Other Simplifications
E+0 −→ E E+-A −→ 0 E*0 −→ 0 E-0 −→ E E-E −→ 0 Aˆ0 −→ 1 E*1 −→ E A*(1/A) −→ 1 B∧false −→ false A/1 −→ A A/A −→ 1 B∨true −→ true Aˆ1 −→ A B⇔true −→ B B∧true −→ B B⇔false −→ ¬B B∨false −→ B B ⇒f alse −→ ¬B B⇒true −→ true true⇒B −→ true f alse⇒B −→ B
Figure 3.6: Summary ofevaluation transformationsduring Normalisation. Erepresents arbitrary expressions,Ainteger expressions andBBoolean expressions
Other Normalisations include the transformation to negation normal form, i.e. every
expression is transformed such that negation is only applied to atomic expressions. For instance,¬(A∨B)is transformed to¬A∧¬Bby applying de Morgan’s Law. Furthermore, we unify inequality operators. For instance, expressions of the formA≥Bare transformed toB ≤A.
Type Adaptions
Constraint solvers support different kinds of types, in particular different types of arrays or domains. Therefore, the types used in a problem instance must be adapted to the solver’s repertory. For instance, some solvers only support 1-dimensional arrays. Hence, if a model is tailored to such a solver, all multi-dimensional arrays have to be flattened to 1- dimensional arrays. Type adaptions typically involve the whole constraint model, therefore it is best performed during preprocessing, when the quantifications are not unrolled and thus the expressions are represented in a compact way (i.e. less expression trees need to be transformed).
Adapting Arrays Arrays are data structures to contain variables of the same type (and
typically from the same category) into one structure. Typically, solver-independent mod- elling languages support multi-dimensional arrays that can be arbitrarily indexed. However, most solvers provide limited support for arrays, which needs to be taken into account during tailoring.
The first limitation concernsdereferencing of arrays. In most solvers, arrays are indexed starting from a fixed constant. For instance, in solver MINION, arrays are indexed starting from ‘0’; in FlatZinc format arrays are indexed from ‘1’. However, in ESSENCE!, the
user can specify the value from which an array is indexed. Therefore, during tailoring, the index domain has to be adapted. For instance, if the index domain of arrayM is defined as int(1..10), but the target solver initialises arrays with ‘0’, the index domain is transformed
intoint(0..9). Changing the index domain also requires changing the dereferences of array
expressions. For instance, the array dereference in the constraint
f i n d M : matrix indexed by [i n t ( 1 . . 1 0 ) ] of i n t( 1 . . 2 0 ) such t h a t f o r a l l i:i n t( 1 . . 1 0 ) . M[i] = i has to be rewritten to f i n d M : matrix indexed by [i n t ( 0 . . 9 ) ] of i n t( 1 . . 2 0 ) such t h a t f o r a l l i:i n t( 1 . . 1 0 ) . M[i−1] = i
Note that only the dereferencing expression (‘M[i−1]’) can be adapted and not the quan-
tifying domain (‘int (1..10)’). Changing the quantifying domain may have serious effects on dereference expressions of other arrays in the quantified expression. As an example, consider a similar constraint below, that involves arrayM together with another array N whose indices range fromint(0..20):
f i n d M : matrix indexed by [i n t ( 1 . . 1 0 ) ] of i n t( 1 . . 2 0 )
f i n d N : matrix indexed by [i n t ( 0 . . 2 0 ) ] of i n t( 1 . . 1 0 )
such t h a t
f o r a l l i:i n t( 1 . . 1 0 ) .
M[i] = N[i]
Adapting the quantifying domain, ‘int (1..10)’, instead of the dereferenced expression of M,‘M[i]’, yields the following constraint that does not preserve the semantics of the initial
constraint: f i n d M : matrix indexed by [i n t ( 1 . . 1 0 ) ] of i n t( 1 . . 2 0 ) f i n d N : matrix indexed by [i n t ( 0 . . 2 0 ) ] of i n t( 1 . . 1 0 ) such t h a t f o r a l l i:i n t ( 0 . . 9 ) . M[i] = N[i]
Note that array index adaptions not only involve transforming array dereferences, but can also involve argument changes in global constraints, such as the element constraint,
element(M,x,y), denotingM[x] = y. In such a case, the constraint has to be transformed
toelement(M,x−1,y).
The second limitation is thearray dimension, for instances, many solvers only support 1- dimensional arrays. Array dimensions can be adapted in the following way: whenever the solver profile indicates that the target solver provides limited support, e.g. only supports 1-dimensional arrays, then every multi-dimensional arrayM is flattened in two steps: first, all constraint expressions are searched for references of M, which are typically of the form M[c1, ..., cn]. Those dereferencing expressions are transformed to dereference a 1-
dimensional array:
M[c1, c2, . . . , cn−1, cn] −→ M[cn+!in−1..1ci∗"ji..n−1(ubj −lbj+ 1)]
where lbi and ubi are the lower and upper bound of the ith index domain of array M.
Consecutively, the entry ofM in the symbol table is changed to an 1-dimensional arrayM: M: matrix indexed by [int(lb1..ub1), int(lb2..ub2), ..., int(lbn..ubn)] −→
M: matrix indexed by [int(lbsolver.. ("1i..nubi−lbi+1)−(1−lbsolver))]
Array Adaption: Example As an example, we consider the array adaption steps when
tailoring a simple instance(below) to FlatZinc format. In FlatZinc, index domains start with ‘1’ and only 1-dimensional arrays are supported. Hence, the 3-dimensional arrayM requires adaption of its index domains, as well as of its dimension.
$ u n a d a p t e d c o n s t r a i n t i n s t a n c e w i t h 3−d i m e n s i o n a l a r r a y $
f i n d M: matrix indexed by [i n t( 1 . . 5 ) ,i n t ( 0 . . 9 ) ,i n t ( 2 . . 4 ) ] of i n t( 1 . . 1 0 )
such t h a t
f o r a l l i:i n t ( 1 . . 5 ) . f o r a l l j:i n t ( 0 . . 9 ) . 10 <= sum k:i n t( 2 . . 4 ) . M[i,j,k]
First, index domains are adapted: the second and third index domain,‘int(0..9’ and‘int (2..4)’ are adapted as follows:
$ STEP1 : a d a p t e d a r r a y d e r e f e r e n c e s t o s t a r t i n g i n d e x ‘1 ’ $
f i n d M: matrix indexed by [i n t( 1 . . 5 ) ,i n t ( 1 . . 1 0 ) ,i n t ( 1 . . 3 ) ] of i n t( 1 . . 1 0 )
such t h a t
f o r a l l i:i n t ( 1 . . 5 ) . f o r a l l j:i n t ( 0 . . 9 ) . 10 <= sum k:i n t( 2 . . 4 ) . M[i,j+1 ,k−1]
Second, the 3-dimensional array has to be flattened to a 1-dimensional array, which yields:
$ STEP2 : f l a t t e n e d m u l t i−d i m e n s i o n a l a r r a y t o 1−d i m e n s i o n a l a r r a y $ f i n d M : matrix indexed by [ 1 . . 1 5 0 ] of i n t( 1 . . 1 0 )
such t h a t
f o r a l l i:i n t ( 1 . . 5 ) . f o r a l l j:i n t ( 0 . . 9 ) .
Adapting Domains The basic domain types are integer and Boolean, however, solvers distinguish between integer domains that represent a range of integers (e.g. int(1..5)) and domains that represent a range with holes (e.g. int(1,3,5)). We will refer to the former
as bound domain and to the latter as sparse domain. Practically every constraint solver
supports bound domains, but some have no support for sparse domains. In that case, sparse domains have to be converted into bound domains with additional disequality constraints that explicitly set the holes in the domain.
Algorithm 3.1FLATTEN INSTANCE(MS)flattens instanceMS to flat instanceMS!.
Require: MS: problem instance
1: globalflatConstraints, constraintBuffer, auxVars←empty lists
2: for allE∈MS.constraintsdo
3: constraintBuffer ←empty 4: E0! ←FLATTEN(Efalse) 5: ES! ←E0! ∧( # iEi!∈constraintBuffer) 6: flatConstraints.add(E!S) 7: MS!.constraints←flatConstraints 8: MS!.vars← {MS.vars∪auxVars} 9: return M!
S