8.4 Built-in Constraints
The following section summarises the built-in constraint predicates of the
ic library.
- Vars :: Domain
-
Constrains Vars to take only integer or real values from the domain
specified by Domain. Vars may be a variable, a list, or a submatrix (e.g.
M[1..4, 3..6]); for a list or a submatrix, the domain is applied recursively
so that one can apply a domain to, for instance, a list of lists of
variables. Domain can be specified as a simple range Lo .. Hi, or as a list
of subranges and/or individual elements (integer variables only). The type
of the bounds determines the type of the variable (real or integer). Also
allowed are the (untyped) symbolic bound values inf, +inf and
-inf.
- Vars $:: Domain
-
Like ::/2, but for declaring real variables (i.e. it never imposes
integrality, regardless of the types of the bounds).
- Vars #:: Domain
-
Like ::/2, but for declaring integer variables.
- reals(Vars)
-
Declares that the variables are IC variables (like declaring
Vars :: -inf..inf).
- integers(Vars)
-
Constrains the given variables to take integer values only.
|
Figure 8.1: Domain constraints
The most common way to declare an IC variable is to use the
::/2 predicate (or
$::/2 or
#::/2) to give it an
initial domain:
?- X :: -10 .. 10.
X = X{-10 .. 10}
Yes
?- X :: -10.0 .. 10.0.
X = X{-10.0 .. 10.0}
Yes
?- X #:: -10 .. 10.
X = X{-10 .. 10}
Yes
?- X $:: -10 .. 10.
X = X{-10.0 .. 10.0}
Yes
?- X :: 0 .. 1.0Inf.
X = X{0 .. 1.0Inf}
Yes
?- X :: 0.0 .. 1.0Inf.
X = X{0.0 .. 1.0Inf}
Yes
?- X :: [1, 4 .. 6, 9, 10].
X = X{[1, 4 .. 6, 9, 10]}
Yes
Note that for ::/2 the type
of the bounds defines the type of the variable (integer or real) but that
infinities are considered type-neutral. To just declare the type of a
variable without restricting the domain at all, one can use the
integers/1
and reals/1.
The final way to declare that a variable is an IC variable is to just use it
in an IC constraint: this performs an implicit declaration.
- ExprX #= ExprY
-
ExprX is equal to ExprY. ExprX and ExprY are integer expressions, and the
variables and subexpressions are constrained to be integers.
- ExprX #>= ExprY
-
ExprX is greater than or equal to ExprY. ExprX and ExprY are integer
expressions, and the variables and subexpressions are constrained to be
integers.
- ExprX #=< ExprY
-
ExprX is less than or equal to ExprY. ExprX and ExprY are integer
expressions, and the variables and subexpressions are constrained to be
integers.
- ExprX #> ExprY
-
ExprX is greater than ExprY. ExprX and ExprY are integer expressions, and
the variables and subexpressions are constrained to be integers.
- ExprX #< ExprY
-
ExprX is less than ExprY. ExprX and ExprY are integer expressions, and the
variables and subexpressions are constrained to be integers.
- ExprX #\= ExprY
-
ExprX is not equal to ExprY. ExprX and ExprY are integer
expressions, and the variables are constrained to be integers.
- ac_eq(X, Y, C)
-
Arc-consistent implementation of X #= Y + C. X and Y are
constrained to be integer variables and to have “reasonable” bounds. C
must be an integer.
|
Figure 8.2: Integral Arithmetic constraints
- ExprX $= ExprY
-
ExprX is equal to ExprY. ExprX and ExprY are general expressions.
- ExprX $>= ExprY
-
ExprX is greater than or equal to ExprY. ExprX and ExprY are general
expressions.
- ExprX $=< ExprY
-
ExprX is less than or equal to ExprY. ExprX and ExprY are general expressions.
- ExprX $> ExprY
-
ExprX is greater than ExprY. ExprX and ExprY are general expressions.
- ExprX $< ExprY
-
ExprX is less than ExprY. ExprX and ExprY are general expressions.
- ExprX $\= ExprY
-
ExprX is not equal to ExprY. ExprX and ExprY are general expressions.
|
Figure 8.3: Non-Integral Arithmetic Constraints
The basic IC relational constraints come in two forms. The first form is
for integer-only constraints, and is summarised in
Figure 8.2. All of these constraints contain
# in their name, which indicates that all numbers appearing in
them must be integers, and all variables and subexpressions will be
constrained to be integral. It is important to note that subexpressions are
constrained to be integral, because it means, for instance, that
X/2 + Y/2 #= 1 and X + Y #= 2 are different
constraints, since the former constrains X and Y to be even.
The second form is the general form of the constraints, and is summarised in
Figure 8.3. These constraints can be used with either
integer or real variables and numbers. With the exception of integrality
issues, the two versions of each constraint are equivalent. Thus if the
constants are integers and the variables and subexpressions are integral,
the two forms may be used interchangeably.
Most of the basic constraints operate by propagating bound information
(performing interval reasoning). The exceptions are the disequality (not
equals) constraints and the ac_eq/3 constraint, which perform
domain reasoning (arc consistency). An example:
?- [X, Y] :: 0 .. 10, X #>= Y + 2.
X = X{2 .. 10}
Y = Y{0 .. 8}
There is 1 delayed goal.
Yes
In the above example, since the lower bound of Y is 0 and
X must be at least 2 greater, the lower bound of X has
been updated to 2. Similarly, the upper bound of Y has been
reduced to 8. The delayed goal indicates that the constraint is still
active: there are still some combinations of values for X and
Y which violate the constraint, so the constraint remains until it
is sure that no such violation is possible.
Note that if a domain ever becomes empty as the result of propagation (no
value for the variable is feasible) then the constraint must necessarily
have been violated, and the computation backtracks.
For a disequality constraint, no deductions can be made until
there is only one variable left, at which point (if it is an integer
variable) the variable's domain can be updated to exclude the relevant
value:
?- X :: 0 .. 10, X #\= 3.
X = X{[0 .. 2, 4 .. 10]}
Yes
?- [X, Y] :: 0 .. 10, X - Y #\= 3.
X = X{0 .. 10}
Y = Y{0 .. 10}
There is 1 delayed goal.
Yes
?- [X, Y] :: 0 .. 10, X - Y #\= 3, Y = 2.
X = X{[0 .. 4, 6 .. 10]}
Y = 2
Yes
For the ac_eq/3 constraint, “holes”
in the domain of one variable are propagated to the other:
?- [X, Y] :: 0 .. 10, ac_eq(X, Y, 3).
X = X{3 .. 10}
Y = Y{0 .. 7}
There is 1 delayed goal.
Yes
?- [X, Y] :: 0 .. 10, ac_eq(X, Y, 3), Y #\= 4.
X = X{[3 .. 6, 8 .. 10]}
Y = Y{[0 .. 3, 5 .. 7]}
There is 1 delayed goal.
Yes
Compare with the corresponding bounds consistency constraint:
?- [X, Y] :: 0 .. 10, X #= Y + 3, Y #\= 4.
X = X{3 .. 10}
Y = Y{[0 .. 3, 5 .. 7]}
There is 1 delayed goal.
Yes
-
⊙
- IC supports a range of mathematical operators beyond the basic
+/2, -/2, */2, etc. See the IC chapter in
the Constraint Library Manual for full details.
-
⊗
- If one wishes to construct an expression to use in an IC constraint at
run time, then one must wrap it in eval/1:
|
?- [X, Y] :: 0..10, Expr = X + Y, Sum #= Expr.
number expected in set_up_ic_con(7, 1, [0 * 1, 1 * Sum{-1.0Inf .. 1.0Inf}, -1 * (X{0 .. 10} + Y{0 .. 10})])
Abort
?- [X, Y] :: 0..10, Expr = X + Y, Sum #= eval(Expr).
X = X{0 .. 10}
Y = Y{0 .. 10}
Sum = Sum{0 .. 20}
Expr = X{0 .. 10} + Y{0 .. 10}
There is 1 delayed goal.
Yes
Reification provides access to the logical truth of a constraint
expression and can be used by:
-
The ECLiPSe system to infer the truth value, reflecting the value
into a variable.
- The programmer to enforce the constraint or its negation by giving a
value to the truth variable.
This logical truth value is a boolean variable (domain 0..1), where
the value 1 means the constraint is or is required to be true, and the value
0 means the constraint is or is required to be false.
When constraints appear in an expression context,
they evaluate to their reified truth value. Practically, this means that
the constraints are posted in a passive check but do not propagate mode.
In this mode no variable domains are modified but checks are made to
determine whether the constraint has become entailed (necessarily true) or
disentailed (necessarily false).
The simplest and arguably most natural way to reify a constraint is to
place it in an expression context (i.e. on either side of a $=
,
#=
, etc.) and assign its truth value to a variable. For example:
?- X :: 0 .. 10, TruthValue $= (X $> 4).
TruthValue = TruthValue{[0, 1]}
X = X{0 .. 10}
There is 1 delayed goal.
Yes
?- X :: 6 .. 10, TruthValue $= (X $> 4).
TruthValue = 1
X = X{6 .. 10}
Yes
?- X :: 0 .. 4, TruthValue $= (X $> 4).
TruthValue = 0
X = X{0 .. 4}
Yes
All the basic relational constraint predicates also come in a three-argument
form where the third argument is the reified truth value, and this form can
also be used to reify a constraint directly. For example:
?- X :: 0 .. 10, $>(X, 4, TruthValue).
X = X{0 .. 10}
TruthValue = TruthValue{[0, 1]}
There is 1 delayed goal.
Yes
As noted above the boolean truth variable corresponding to a constraint can
also be used to enforce the constraint (or its negation):
?- X :: 0 .. 10, TruthValue $= (X $> 4), TruthValue = 1.
X = X{5 .. 10}
TruthValue = 1
Yes
?- X :: 0 .. 10, TruthValue $= (X $> 4), TruthValue = 0.
X = X{0 .. 4}
TruthValue = 0
Yes
By instantiating the value of the reified truth variable, the constraint
changes from being passive to being active. Once actively
true (or actively false) the constraint will prune domains as though
it had been posted as a simple non-reified constraint.
-
⊙
- Additional information on reified constraints can be found in the
ECLiPSe Constraint Library Manual that documents IC: A Hybrid
Finite Domain / Real Number Interval Constraint Solver.
- and
-
Constraint conjunction. e.g. X $> 3 and X $< 8
- or
-
Constraint disjunction. e.g. X $< 3 or X $> 8
- =>
-
Constraint implication. e.g. X $> 3 => Y $< 8
- neg
-
Constraint negation. e.g. neg X $> 3
|
Figure 8.4: Constraint Expression Connectives
IC also provides a number of connectives useful for combining constraint
expressions. These are summarised in Figure 8.4.
For example:
?- [X, Y] :: 0 .. 10, X #>= Y + 6 or X #=< Y - 6.
X = X{0 .. 10}
Y = Y{0 .. 10}
There are 3 delayed goals.
Yes
?- [X, Y] :: 0 .. 10, X #>= Y + 6 or X #=< Y - 6, X #>= 5.
Y = Y{0 .. 4}
X = X{6 .. 10}
There is 1 delayed goal.
Yes
In the above example, once it is known that X #=< Y - 6 cannot be
true, the constraint X #>= Y + 6 is enforced.
Note that these connectives exploit constraint reification, and actually
just reason about boolean variables. This means that they can be used as
boolean constraints as well:
?- A => B.
A = A{[0, 1]}
B = B{[0, 1]}
There is 1 delayed goal.
Yes
?- A => B, A = 1.
B = 1
A = 1
Yes
?- A => B, A = 0.
B = B{[0, 1]}
A = 0
Yes