610 likes | 720 Views
Indexical Constraints. A key issue in Constraint (Logic) Programming system, is the implementation of the underlying Constraint Solvers. A number of features of these solvers must be taken into account: The ease in representing domains and generic constraints;
E N D
Indexical Constraints • A key issue in Constraint (Logic) Programming system, is the implementation of the underlying Constraint Solvers. A number of features of these solvers must be taken into account: • The ease in representing domains and generic constraints; • The possibility of controling constraint propagation in the ways appropriate to the applications; • The possibility of combining simpler constraints into more complex constraints; • The transparency of the implementation (glass-box) allowing the users to acceed the primitive constraints to implement user specific constraints and variable and value heuristics.
Indexical Constraints An adequate way of satisfying all these constraints has been the use of constraint solvers based in indexical constraints. This is, for example, the methodology adopted in the implementation of SICStus (as well as GNU Prolog), specifically in their finite domains module. Indexical constraints have a compact and integrated approach to representing both the domains and the constraints of the problem variables. To be useful, indexical constraints require that variables domains have an ordering. In fact, SICStus requires that the domains are finite subsets of the integers.
Indexical Constraints • Introductory example: • Let us consider the non-strict inequality constraint, , applied to variables A and B whose initial domains are respectively 2000 to 5000 and 1000 to 4000. In SICStus syntax • A in 2000..5000, B in 1000..4000, A #=< B • This example will enable an analysis of the advantages and disadvantages of the possible implementation methods, namely to issues such as • Maintaining the variables domains; • Representing the constraints; • The adequate propagation of these constraints.
Indexical Constraints A in 2000..5000, B in 1000..4000, A #=< B Concerning the domains, a first issue is whether to adopt a representation by intension or by extension. Notice that during propagation domains may only be reduced, they never increase! The representation by extension maintains explicitely, in some data structure, all current values in the domains. As domains may only decrease, such data structure may be static, for example a Boolean vector (bit array). Such option suffers from many disadvantages. For example detection of failures (i.e. empty domains) requires either the traversal of the whole data structure (3000 memory positions!), or the maintenance of “bit counters”.
Indexical Constraints A in 2000..5000, B in 1000..4000, A #=< B Given the importance of an effective detection of failures, and the memory overhead (domain values may usually be represented in a more compact form) it is thus more adequate, for a general purpose solver, to adopt a representation by intension. In the beginning, one may always consider explicit limits for the domains, all that is required in domains declaration. Detection of failure is then very simple: the upper limit of a domain cannot be less than the lower limit. This representation is particularly efficient as long as the domains are kept convex (i.e. with no holes).
A #=< B Indexical Constraints A in 2000..5000, B in 1000..4000, A #=< B Most usual constraints maintain such convexity, namely inequality (and equality) constraints. Constraint A #=< B is satisfiable as long as the minimum value of the domain of A (in short, the lower bound of A) is not greater than the upper bound of B. On the other hand, no value from the domain of A may be higher then the upper bound of B, i.e. the upper bound of A must be no greater than the upper bound of B. Similarly, the lower bound of B must be not less than the lower bound of A).
Indexical Constraints A in 2000..5000, B in 1000..4000, A #=< B As these operations are quite common (e.g. in numerical applications), it is necessary to make the appropriate connection between these operations on the upper/lower bounds and the constraints that enforce them to achieve the adequate propagation. In indexical constraints, this link is achieved by specifying domains bounds as a function of other bounds. Constraints are thus represented by expressions on the variables bounds. Since the domains can only be reduced, such constraints are implemented by representing the bounds of variables as a function of the bounds of other variables and max/min operations to implement intersection.
A #< B Indexical Constraints A in 2000..5000, B in 1000..4000, A #=< B Posting the constraint A #=< B changes the initial domains of variables A and B Domain of A 2000 .. 5000 inf .. max(B) ... i.e. max(A) = min(5000, max(B)) = max(B) Domain of B 1000 .. 4000 min(A) .. sup ... i.e. min(B) = max(1000, min(A)) = min(A) • The constraint is thus implicitely represented as • A in 2000 .. 4000 inf ..max(B) • B in 2000 .. 4000 min(A) ..sup
Indexical Constraints • A in 2000..5000, B in 1000..4000, A #=< B • The implicit constraint representation • A in 2000 .. 4000 inf ..max(B) • B in 2000 .. 4000 min(A) ..sup • also allows the intended propagation. Whenever the upper bound of B changes (i.e. decreases!), such condition is propagated to variable A, whose upper bound also decreases (from 5000 to 4000). Comparison with the lower bound of A (2000) detects when the domain of A becomes empty ! • Propagation from A to B is similar. Any change in the lower bound of A is propagated to the lower bound of B. Changes in A’s upper bound and B’s lower bound are not propagated.
Indexical Constraints Propagation may be observed in the introduction of a new variable C, constraining B, C in 3000..3500,B #= C 0 A in 2000 .. 4000 inf ..max(B) B in 2000 .. 4000 min(A) ..sup Upon posting the domain of C 1 C in 3000 .. 3500 Upon posting the constraint C #=< B 2a C in 3000 .. 3500 min(B) .. max(B) 2b B in 2000 .. 4000 [min(C).. max(C) min(A)..sup)] 2000 .. 4000max(min(C),min(A)) .. min(max(C),sup) 3000 .. 3500max(min(C),min(A)) .. max(C) 2c A in 2000 .. 3500 inf ..max(B) Simplifications are made, if possible. The upper bound of B is simplified, but not its lower bound (the greater of the lower bounds of A and C is not determined yet).
Indexical Constraints • Of course, the user must not be concerned, in general, with this low level implementation. • The most usual constraints, concerning the usual arithmetic operations and relational operations, are directly compiled in indexical constraints. • For example, constraint A + B #= C is compiled in the following indexical constraints • C in min(A)+min(B) .. max(A)+max(B) • A in min(C)-max(B) .. max(C)-min(B) • B in min(C)-max(A) .. max(C)-min(A) • Notice the monotonic nature of domain operations. The lower bound of A increases with the increase of the lower bound of C and the decrease of the upper bound of B.
Indexical Constraints The previous examples show that the type of consistency maintained by indexical constraints in constraints of equality (#=) and inequality, strict or not, (#<, #=<, #> and #>=) is, by default, bounds (or interval) consistency. The default compilation of the constraints only affects the bounds of the domains of the variables. Nevertheless, there is the possibility of imposing other types of consistency adequate for many other constraints, namely node consistency and arc consistency. These types of consistency have only interest when it is important to consider concave domains (with “holes”), as for example in the queens and graph colouring problems.
Indexical Constraints • Concavities are possibly introduced by disequality constraints (#\=). Let us consider, for example, • A in 1..9, B in 3..5, A #\= B • This disequality constraint has to be represented through set complement operations, not intersection. Adopting SICStus syntax • A in \dom(B) e B in \dom(A) • This operation raises problems regarding monotonicity, since when the domain of one variable decreases the domains of the oter would increase! • A in 1 .. 9 \dom(B) • B in 3 .. 5 \dom(A)
Indexical Constraints • A in 1..10, B in 3..5, A #\= B • A in 1 .. 9 \dom(B) • B in 3 .. 5 \dom(A) • For this reason, complement operations are “frozen” until the domains to be complemented are reduced to singletons. • This actually implements node consistency, the most useful criterion to handle disequality constraints. • When the domain of B becomes a singleton, for example B=3, the domain of A becomes concave, being represented by set union. In SICStus syntax • ?- A in 1..9, B in 3..5, A #\= B, B #=3, fd_dom(B,S). • B = 3, • S = {3}, • A in(1..2)\/(4..9) ?
Indexical Constraints Arc consistency requires that whenever the domain of a variable (not only its bounds) is changed, a check is made on whether the other variables still maintain support. Arc consistency is thus implemented by specifying in the indexical constraints the domain of the other variables. For example, in SICStus syntax, if it is required to maintain arc consistency on the previous sum constraint, the constraint can be specified by the user by means of the following FD predicate sum(A,B,C)+: A in dom(C) - dom(B), B in dom(C) - dom(A), C in dom(A) + dom(B).
Indexical Constraints User defined constraints in SICStus (by means of FD predicates) is thus similar to the predicate definitions in Prolog, with a different “neck” operator (“+:” rather than the Prolog operator “:-”). There are however some differences, namely there are no alternative “clauses”, i.e. no backtracking in constraint definitions. Using the previous definition we would get, ?- A in 1..6, A#\=3,A#\=4,A#\=5, B in 1..2, sum(A,B,C). A in(1..2)\/{6}, B in 1..2, C in(2..4)\/(7..8) ?% A+B#= C C in 1..8 ?- C in 1..9, C#\=5,C#\=6,C#\=7, B in 0..2, sum(A,B,C). C in(1..4)\/(8..9), B in 0..2, A in(-1..4)\/(6..9) ? % A+B#= C A in -1..9
Indexical Constraints Indexical expressions may only appear in the “body” of FD predicate definitions. Such limitation is due to the different execution mechanisms between the host language (Prolog) and the FD module in SICStus. In fact, given the sum definition soma(A,B,C)+: A in dom(C) - dom(B), B in dom(C) - dom(A), C in dom(A) + dom(B). it is intended that, in the FD module, the domains of A/B/C are reevaluated by updates on the domains of C,B/C,A/A,B. In contrast with Prolog execution, the primitives appearing in indexical expressions (e.g. dom, max, min, etc) should be regarded as reactive agents (to changes).
Indexical Constraints • In addition to the primitives already examplified, indexical expressions may be formed with FD terms. Being X a domain variable and D(X) its current domain, the basic FD terms are • min(X) minimum of D(X) • max(X) maximum of D(X) • card(X) cardinality of D(X) • X value (integer) of X. The expression is only evaluated when X is instantiated. • I an integer • inf minus infinity • sup plus infinity
Indexical Constraints • FD terms, basic or not, may be combined through arithmetic operators into compound FD terms. Denoting T1/T2 arbitrary FD terms, and D(T1)/D(T2) their current “domains”, the following compound terms may be formed (and evaluated by interval arithmetic) • -T1 I-negation ofD(T1) • T1+T2 I-sum of D(T1) and D(T2) • T1-T2 I-difference of D(T1) and D(T2) • T1*T2 I-product of D(T1) and D(T2), D(T2) >= 0 • T1/>T2 D(T1) div D(T2), with outbound rounding, T1/<T2 with D(T2) >= 0 • T1 mod T2D(T1) mod D(T2)
Indexical Constraints • FD terms are used in indexical consraints to define ranges for the domain variables, that must be intersected with the current domains of these variables. In general, indexical constraints take the form • X in R • where R is a range defined by the following grammar. The basic ranges are the following • dom(X) D(X) • {T1,...,Tn}set of terms Ti • T1..T2interval bound by terms Ti • where X denotes a domain variable and Ti an FD term. Ohter terms may subsequently be obtained by composition.
Indexical Constraints • Denoting by Ri a range and by D(Ri) the corresponding domain, the following operators are used to compose terms • R1/\R2 intersection of D(R1) and D(R2) • R1\/R2 union of D(R1) e D(R2) • \R1 complement of D(R1) • R1+R2 (T2) I-sum of D(R1) and D(R2) (or D(T2)) • -R1 I-negation of D(R1) • R1-R2 e R1-T2 I-difference of D(R1) and D(R2) (D(T2)) • R1 mod R2 (T2) I-mod of D(R1) and D(R2) (D(T2)) • R1 ? R2if R1 \= then D(R2) else • unionof(X,R1,R2) union of D(Sk): each Sk is obtained from R2 replacing X for the k-th element of R1.
Indexical Constraints Example 1: adjust(X,Z,K,N) For variables X and Z, with domínio 1 a N, make X to be shifted from Z by a value K (0=< K < N), with rewrapping. This kind of adjust enables that, while enumerating Z in the “standard” increasing order, X is enumerated with the “shifted” ordering. For example, let us assume N = 15 and K = 5, i.e. that while Z is enumerated with the standard increasing order X is enumerated with the order shifted by 5
Indexical Constraints Example 1: adjust(X,Z,K,N) Notice that X=((Z+K+N-1)mod N)+1 and Z=((X-K+N-1)mod N)+1. For example, for Z = 7 X = 12 as intended X = (7+5+15-1) mod 15 +1 = 26 mod 15 +1 = 11 +1 = 12 Z = (12-5+15-1) mod 15 + 1= 21 mod 15 + 1= 6 +1 = 7 Hence, we may formulate the correspondence between X and Z with the following specification Solution 1: adjust(X,Z,K,N)+: X in ((dom(Z)+K+N-1) mod N)+1, Z in ((dom(X)-K+N-1) mod N)+1.
Indexical Constraints Example 1: adjust(X,Z,K,N) In fact, the mod operation is unnecessary, if X and Z are defined over 1..N. All that is required is, for each value Z to add K. Since this may result above the upper bound of X, sometimes N must be subtracted. For example: for Z = 7 X1 = 7 + 5 = 12 and X2 = 7 + 5 - 15 =-3 for Z = 11 X1 = 11 + 5 = 16 and X2 = 11 + 5 - 15 = 1
Indexical Constraints By using the union of the two intervals, one resulting from simply adding K and the other by adding K and subtracting N, the adequate value of X is thus obtained. This leads to the simpler formulation. Solution 2: adjust(X,Z,K,N)+: X in (dom(Z)+ K) \/(dom(Z)+ K - N), Z in (dom(X)- K) \/(dom(X)- K + N). Task: Enumerate with an ordering from N/2 to the “edges”.
Indexical Constraints Example 2: no_attack(L1,C1,L2,C2) Two queens, placed in rows L1 and L2 (constants) and columns C1 e C2 (domain variables) should not attack each other, i.e. C1 \= C2, L1+C1 \= L2+C2 and L1+C1 \= L2+C2. Solution 1: no_attack(L1,C1,L2,C2)+: C1 in \({C2}\/{C2+L2-L1}\/{C2+L1-L2}), C2 in \({C1}\/{C1+L1-L2}\/{C1+L2-L1}). In such approach, as the domain variables appear in a negative “context” (\{C}) the constraint is frozen until C is instantiated. Hence, this formulation guarantees the maintenance of node consistency. In alternative, one may specify arc consistency.
Indexical Constraints Solution 2: no_attack(L1,C1,L2,C2)+: C1 in unionof(Y,dom(C2),\({Y}\/{Y+L2-L1}\/{Y+L1-L2})), C2 in unionof(X,dom(C1),\({X}\/{X+L1-L2}\/{X+L2-L1})). Here, we get arc consistency by allowing a queen to be in a position that is not under the attack of at least one possible position of the other queen, which supports the former. For example, the domain of C1 is obtained by the union of all values that are “safe” for some value in the domain of C2, through a range expression that takes in turn all values of the domain of C2, renaming them as Y C1 in unionof(Y,dom(C2),...).
Indexical Constraints Solution 3: no_attack(L1,C1,L2,C2)+: C1 in (4..card(C2)) ? (inf..sup) \/ unionof(Y,dom(C2),\({Y} \/ {Y+L2-L1} \/ {Y+L1-L2})), C2 in (4..card(C1)) ? (inf..sup) \/ unionof(X,dom(C1),\({X} \/ {X+L1-L2} \/ {X+L2-L1})). This third approach optimises arc consistency in this problem. Taking into account that a position in a queen is always supported by a queen with 4 or more positions available, arc consistency is only evaluated when the cardinality of the domain drops below 4. In fact, C1 is constrained to be either inf..sup (when the cardinality of D(C2) is >=4) or unionof(Y,... )otherwise.
Disjunctive Constraints Disjunctive Constraints and Backtracking In general, the execution of a CLP problem includes a search phase with backtracking on the enumeration of the variables when the constraints and their propagation is not sufficient to eliminate redundant values from the variables domains. This backtracking occurs in the enumeration phase, when all the constraints are already posted, i.e. There is no backtracking on constraint posting. However, when constraints are specified by intension, the most natural way to combine them is through the disjunction of the different alternatives. One must then identify the best way to specify disjunction.
Disjunctive Constraints • Example: Given two tasks, T1 and T2, with stating times S1 and S2 and duration D1 and D2, garantee that they do not overlap. • A natural implemention of this constraint is through the equivalent disjunction • T2 does not start before the end of T1; or • T1 does not start before the end of T2 • The specification of this constraint, namely the disjunction of the alternatives, may use the usual disjunction of Prolog clauses (and the associated backtracking) • disjoint(S1, S2, D1, D2) :- S1 + D1 #=< S2. • disjoint(S1, S2, D1, D2) :- S2 + D2 #=< S1.
Disjunctive Constraints • Problem(Vars):- • Declaration of Variables and Domains, • Specification of Constraints, • Labelling of the Variables. • This formulation raises the problem of interacting backtrack mechanisms made • On the phase of specification and posting of constraints • On the enumeration phase of the variables. • With k tasks that should not overlap, there are k*(k-1)/2 combinations of precedence between pairs of tasks. With 50 tasks, K=50, there will be 50*49/2 = 1225 constraints and 21225 ways of combining them (in fact only 50!).
Disjunctive Constraints • Problem(Vars):- • Declaration of Variables and Domains, • Specification of Constraints, • Labelling of the Variables. • Hence the two alternatives • 1 single problem with K variables, with complexity O(dk) • Or a formulation with alternative “problems” on the same variables leading to • K! problems on these variables, with complexity O(k! dk) • each of the K problems corresponding to an ordering of the tasks. Although each such problem is more constrained than the single one, the huge number of problems makes this formulation very inneficient.
Reified Constraints A better formulation uses a single specification of all the constraint alternatives, avoiding the backtracking of multiple constraint definitions. Such formulation can be obtained through the reification of constraints. The basic idea of constraint reification is to associate its satisfaction to a boolean variable 0/1, and make this variable accessible to the program level. For example associating constraints C1 and C2 to boolean variables B1 e B2, the disjunction of these constraints may be specified by means of constraint B1 + B2 #>= 1.
Reified Constraints • Once defined this basic meta-level mechanism of reified constraints, its scope can be enlarged for other types of combinations of constraints, namely those requiring some countings. • For example, if from the 20 constraints C1,..., C20 at least 10 must be satisfied, instead of specifying, in adition to the 20 constraints, C1 to C20 • C1020 = 184 756 alternative combinations of 10 out of 20 constraints • the only additional specifications required are • The constraints reification to Boolean variables B1 a B20 • The “counting” constraint B1 + B2 + ... + B20 #>= 10
Ask & Tell in Reified Constraints • For the correspondance between a constraint C and a Boolean variable B is efficiently exploited, Ask & Tell mechanisms must be defined to • Tell mechanisms • Tell(C) - Post the constraint • Tell(~C) - Post the “negation” of the constraint • Ask mechanisms • Ask(C) - Check the entailment of the constraint • Ask(~C) - Check the entailment of its negation. • In fact, if one and only one of constraints C1 and C2 is to be satisfied all the 4 above situations must be implemented.
Ask & Tell in Reified Constraints • Ask(C1) / Ask(C2) succeeds • Once deteced the satisfaction of one of the constraints, the negation of the other constraint must be posted, i.e. tell(~R2)/tell(~R1). • Ask(~C1) / Ask(~C2) succeeds • Once detected the satisfaction of the negation of one of the constraints, the other constraint must be posted, i.e. tell(C2)/tell(C1). • It is thus important to see how reified constraints, and the underlying Ask&Tell mechanisms may be specified, not only in built-in predefined constraints, but also for user defined constraints.
Propagating and Checking Indexicals • In SICStus the association between constraints and boolean variables is specified through the built-in operator “#<=>” • C #<=> B. • where B is the boolean variable and C the constraint. • Many built-in constraints are pre-defined. Such is the case of linear numerical constraints. • Hence, the non-overlapping of tasks described before could be specified through with no alternative clauses as • disjoint(S1, S2, D1, D2) :- • (S1 + D1 #=< S2) #<=> B1, % T1 before T2 • (S2 + D2 #=< S1) #<=> B2, % T2 before T1 • B1 + B2 #>= 1.
Propagating and Checking Indexicals For constraints defined by the user, the appropriate Ask & Tell conditions must also be defined. For instance, let us consider the constraint of difference between two domain variables. The two Ask mechanisms are imposed by propagating indexical specifications, that include the positive definitions (‘+:’), already known ’x\\=y’(X,Y) +: X in \{Y}, Y in \{X}. As well as negative definitions (‘-:’) ’x\\=y’(X,Y) -: X in dom(Y), Y in dom(X.
Propagating and Checking Indexicals The Tell mechanisms are imposed by checking indexicals, both positive (‘+?’) ’x\\=y’(X,Y)+? X in \dom(Y). And negative (‘-:’) ’x\\=y’(X,Y) +: X in {Y}. The intended behaviour to propagating and checking indexicals, imposes some constraints on the expressions that can be used on the “body” of the definitions, as well as to their execution. Some of these limitations are presented next.
Propagating and Checking Indexicals • Propagating Indexicals (Trigger) • A Propagating Indexical X in R is scheduled for execution whenever • It becomes monotonic for the first time • It is rescheduled whenever, for another variable Y appearing in R • The domain of Y is changed and Y appears in R in the form card(Y) ou dom(Y); • The upper/lower bounds of Y change and Y appears in R in the form max(Y)/min(Y).
Propagating and Checking Indexicals • Propagating Indexicals (Result) • Given some variable X referrred to in an indexical constraint, and denoting by M(X) the value of the constraint in the current state of memory and by I(X) the interval between the lower and upper bounds of X, then • If M(X) is disjoint from I(X), there is a contradiction. • If I(X) is within M(X), there are no values in the current domain of X incompatible with the indexical constraints, whose execution is suspended, until the situation becomes “ground”. • Otherwise, the indexical constraint is added to the constraints over X, whose domain will be pruned.
Propagating and Checking Indexicals Example: Propagating Indexicals Constraint X in \{Y}in the positive propagating indexical is suspended until it gets monotonic, i.e. until Y gets instantiated. In this case, the constraint is either contradictory, or is propagated, pruning the value of Y from the domain of X. Constraint X in dom(Y), in the negative propagating indexical is inherently monotonic. Whilst Y has values in its domain, X is getting prunned. If the domains of X and Y become disjunct, then the negation of the constraint of difference fails! When the domain of Y gets ground, then so does the domain of X and the constraint succeeds.
Propagating and Checking Indexicals • Checking Indexicals (Trigger) • A checking indexical X in R is triggered for execution in the following conditions • It becomes anti-monotonic for the first time • It is rescheduled whenever, • The domain of X has been pruned or got ground; • The domain of another variable Y, appearing in R in the form card(Y) or dom(Y), is pruned; • The upper/lower bounds of another variable Y, appearing in R in the form max(Y)/min(Y), is pruned.
Propagating and Checking Indexicals • Checking Indexicals (Result) • Given some variable X referrred to in an indexical constraint, and denoting by M(X) the value of the constraint in the current state of memory and by I(X) the interval between the lower and upper bounds of X, then • If I(X) is contained in M(X), then no value of X may turn the constraint false, as M(X) may only “increase size” (anti-monotonic). Hence the constraint is entailed. • If I(X) is disjoint from M(X) and M(X) is ground then the constraint may no longer be satisfied, and is disentailed. • Otherwise, the checking indexical is suspended.
Propagating and Checking Indexicals Example: Checking Indexicals Constraint X in \dom(Y)in the positivechecking indexical is inherently anti-monotonic (the values of \dom(Y)may only increase as dom(Y) decreases during execution. Hence, the positive checking indexicalsucceeds as soon as the domain of X has no elements in common with the domain of Y. Constraint X in {Y}, only gets anti-monotonic when Y becomes ground to some value. Whilst X has this value in its domain the constraint suspends. If X only has this value in its domain the negative checking succeeds, i.e. the constraint of difference fails.
’x\\<y’(X,Y)+: X in inf .. max(Y)-1, Y in min(X)+1 .. sup. ’x\\<y’(X,Y)-: %x>=y X in min(Y) .. sup, Y in inf .. max(X). ’x\\<y’(X,Y)+? X in inf .. min(Y)-1, Y in max(X)+1 .. sup. ’x\\<y’(X,Y)-? X in max(Y).. sup, Y in inf .. min(X). Propagating and Checking Indexicals Example 2: Strict Inequality Constraints As can be checked easily, the following definitions of propagating and checking indexicals are adequate to reify a constraint of strict inequality of the form ’x\\<y’(X,Y) #<=> B
Cardinality (Meta-)Constraint Many problems, namely scheduling problems with various types of precedence betwen tasks, have the requirement that among a list of constraints Lr = [C1, C2, ... , Cn] The number of constraints to satisfy must lie between a lower bound L and an upper bound U, both inclusive. Syntactically, this meta-constraints may be specified as #(L,U, [C1, C2, ... , Cn]) In particular, such constraints are quite common in resource management, where resources (or people) must be allocated in a “flexible” way.
Cardinality (Meta-)Constraint Example (Nurse Scheduling): From the 7 nurses available at some hospital ward, at least 4 must be on duty in the first shift. This constraint may be modelled by considering boolean variables Xij to represent that nurse i is on duty in shift j. The intended allocation may be modelled by specification, to each shift j, of the cardinality meta-constraint #(4,7, [X1j=1, X2j=1, ... , X7j=1]) In alternative, 7 variables Nij are considered for each shift j, whose domain are the nurses 0 to 7 (0 meaning no nurse) leading to the formulation #(4, 7, [N1j0, N2j 0, ... , N7j 0])
Cardinality (Meta-)Constraint The operational semantics of this meta-constraint may be specified by rewriting rules, using the Ask & Tell primitives just presented. The first two rules rewrite the constraint into a simplified form Rule 1: If one constraint is satisfied, it may be removed, and the number of constraints to satisfy is decreased by one. #(L,U, [C1, C2, ... , Ci, ... , Cn]), ask(Ci) #(L-1,U-1, [C1, C2,...,Ci-1,Ci+1,..., Cn]) Rule 2: If one constraint is not satisfied, it is simply removed from the list. #(L,U, [C1, C2, ... , Ci, ... , Cn]), ask(~Ci) #(L,U, [C1, C2,...,Ci-1,Ci+1,..., Cn])
Cardinality (Meta-)Constraint The two following rules refer to situations where the satisfaction or “dissatisfaction” of the constraints must be imposed. Rule 3: If the number of constraints in the list is the same as the lower limit, then all constraints must be satisfied. #(L,U, [C1, C2, ... , Ci, ... , Cn]), L = n tell(C1), tell(C2), ..., tell(Cn) Rule 4: If the upper limit becomes 0, all the constraints in the list must be dissatisfied. #(L,U, [C1, C2, ... , Ci, ... , Cn]), U = 0 tell(~C1), tell(~C2), ..., tell(~Cn)