next up previous index
Next: Library Predicates Up: IC: A Hybrid Finite Previous: IC: A Hybrid Finite   Index

Subsections

Introduction

The IC (Interval Constraint) library is a new hybrid integer/real interval arithmetic constraint solver. Its aim is to make it convenient for programmers to write hybrid solutions to problems, mixing together integer and real constraints and variables.

Previously, if one wished to mix integer and real constraints, one had to import separate solvers for each, with the solvers using different representations for the domains of variables. This meant any variable appearing in both kinds of constraints would end up with two domain representations, with extra constraints necessary to keep the two representations synchronised.

When IC and its integration with other ECLiPSe libraries is completed, there will be just the one representation for variables which is common to all numeric solvers. This means users will be able to mix and match any solvers they like without having to worry about underlying representations and synchronisation constraints.

Current Status

The IC library is not finished. Key features which have yet to be implemented include:

The IC solver is included in this release in order to let users try out what has been done so far and give feedback. If you have any comments, questions, suggestions, etc., please let us know.


What IC does

IC is a general interval propagation solver which can be used to solve problems over both integer and real variables. Integer variables behave much like those from the finite domain solver FD, while real variables behave much like those from the real interval arithmetic solver RIA. IC allows both kinds of variables to be mixed seamlessly in constraints, since (with a few exceptions) the same propagation algorithms are used throughout and all variables have the same underlying representation (indeed, a real variable can be turned into an integer variable simply by imposing an integrality constraint on it).

It is intended that eventually IC will replace the `fd', `ria' and `range' libraries. Since IC does not support symbolic domains, there will be also be a new symbolic solver library to provide the non-numeric functionality of `fd'.

Differences between IC and FD

Apart from the fact that only simple labeling predicates have been implemented so far, there are two main differences between integer IC variables and FD variables. The first is that IC is a purely numeric solver, and thus does not support the symbolic domains which FD allows.

The second relates to the treatment of large domain values. In FD, numeric domains are more or less limited to -10000000..10000000 (this default domain can be modified, but the larger one makes it, the more likely one is to run into machine integer overflow problems). In IC there is no limit as such, and bounds on integer variables can be infinite (though variables should not be assigned infinite values). However, since floating point numbers are used in the underlying implementation, not every integer value is representable. Specifically, integer variables and constraints ought to behave as expected until the values being manipulated become large enough that they approach the precision limit of a double precision floating point number (251 or so). Beyond this, lack of precision may mean that the solver cannot be sure which integer is intended, in which case the solver starts behaving more like an interval solver than a traditional finite domain solver. Note however that this precision limit is way beyond what is normally supported by finite domain solvers (typically substantially less than 232). Note also that deliberately working with integer variables in this kind of range is not particularly recommended; the main intention is for the computation to be ``safe'' if it strays up into this region by ensuring no overflow problems.

It should also be mentioned here that integer domains which have had particular values excluded from them are currently represented as bitmaps (this will probably be refined in a future release, in particular to better support sparse domains). For pragmatic reasons, such bitmaps must have finite bounds, and those bounds must be representable in a machine word.

Differences between IC and RIA

Apart from the fact that only a few search predicates have been implemented so far, the main difference between IC's interval solving and RIA's is that IC is aware of and utilises the new bounded real numeric type. This means unifying IC variables with bounded reals poses no problems for IC. In contrast, RIA will fail with a type error if any of its variables are unified with a bounded real.

Notes about interval arithmetic

The main problem with using floating point arithmetic instead of real arithmetic for doing any kind of numerical computation or constraint solving is that it is only approximate. Finite precision means a floating point value may only approximate the intended real; it also means there may be rounding errors when doing any computation. Worse is that one does not know from looking at an answer how much error has crept into the computation; it may be that the result one obtains is very close to the true solution, or it may be that the errors have accumulated to the point where they are significant. This means it can be hard to know whether or not the answer one obtains is actually a solution (it may have been unintentionally included due to errors), or worse, whether or not answers have been missed (unintentionally excluded due to errors).

Interval arithmetic is one way to manage the error problem. Essentially each real number is represented by a pair of floating point bounds. The true value of the number may not be known, but it is definitely known to lie between the two bounds. Any arithmetic operation to be performed is then done using these bounds, with the resulting interval widened to take into account any possible error in the operation, thus ensuring the resulting interval contains the true answer. This is the principle behind the bounded real arithmetic type.

Note that interval arithmetic does not guarantee small errors, it just provides a way of knowing how large the error may have become.

One drawback of the interval approach is that arithmetic comparisons can no longer always be answered with a simple ``yes'' or ``no''; sometimes the only possible answer is ``don't know''. This is reflected in the behaviour of arithmetic comparators (=:=, >=, etc.) when applied to bounded reals which overlap each other. In such a case, one cannot know whether the true value of one is greater than, less than, or equal to the other, and so a delayed goal is left behind. This delayed goal indicates that the computation succeeded, contingent on whether the condition in the delayed goal is true. For example, if the delayed goal left behind was 0.2__0.4 >= 0.1__0.3, this indicates that the computation should be considered a success only if the true value represented by the bounded real on the left is greater than or equal to that of the bounded real on the right. If the width of the intervals in any such delayed goals is non-trivial, then this indicates a problem with numerical accuracy. It is up to the user to decide how large an error is tolerable for any given application.

Interval arithmetic and IC

In order to ensure the soundness of the results it produces, the IC solver does almost all computation using interval arithmetic. As part of this, the first thing done to a constraint when it is given to the solver is to convert all non-integer numbers in it to bounded reals. Note that for this conversion, floating point numbers are assumed to refer to the closest representable float value, as per the type conversion predicate breal/2. This lack of widening when converting floating point numbers to bounded reals is fine if the floating point number is exactly the intended real number, but if there is any uncertainty, that uncertainty should be encoded by using a bounded real in the constraint instead of a float.

One of the drawbacks of this approach is that the user is not protected from the fundamental inaccuracies that may occur when trying to represent decimal numbers with floating point values in binary. The user should be aware therefore that some numbers given explicitly as part of their program may not be safely represented as a bounded real that spans the exact decimal value. eg.|ic:(X =:= 0.1)| or equivalently X is breal(0.1).

This may lead to unexpected results such as

[eclipse 2]: ic:(X =:= 0.1), ic:(Y =:= 0.09999999999999999), ic:(X > Y).

X = 0.1__0.1
Y = 0.099999999999999992__0.099999999999999992
Yes (0.00s cpu)

[eclipse 3]: ic:(X =:= 0.1), ic:(Y =:= 0.099999999999999999), ic:(X > Y).

No (0.00s cpu)

This potential source of confusion arises only with values which are explicitly given within a program. By replacing the assignment to Y with an expression which evaluates to the same real value we get

[eclipse 4]: ic:( X =:= 0.1), ic:(Y =:= 0.1 - 0.000000000000000001), ic:(X > Y).

X = 0.1__0.1
Y = 0.099999999999999992__0.10000000000000002


Delayed goals:
        0.099999999999999992__0.10000000000000002 < 0.1__0.1
Yes (0.00s cpu)

Note the delayed goal indicating the conditions under which the original goal should be considered to have succeeded.

Usage

To load the IC library into your program, simply add the following directive at an appropriate point in your code.

:- lib(ic).

Arithmetic Expressions

The IC library solves constraint problems over the reals. It is not limited to linear constraints. So it can be used to solve general problems like:

[eclipse 2]: ic:(ln(X) >= sin(X)).

X = X{0.36787944117144211 .. 1.0Inf}


Delayed goals:
...
Yes (0.01s cpu)
The IC library treats linear and non-linear constraints differently. Linear constraints are handled by a single propagator, whereas non-linear constraints are broken down into simpler ternary/binary/unary propagators.

Any relational constraint (=:=, >=, #=, etc.) can be reified simply by including it in an expression where it will evaluate to its reified truth value.

User-defined constrains may also be included in constraint expressions where they will be treated in the same manner to user defined functions found in expressions handled by is/2. That is to say they will be called at run-time with an extra argument to collect the result.

The following arithmetic expression can be used inside the constraints:

X
Variables. If X is not yet a interval variable, it is turned into one via an implicit declaration X ::-1.0Inf..1.0Inf.

123
Integer constants. They are assumed to be exact and are used as is.

0.1
Floating point constants. These are assumed to be exact and are converted to a zero width bounded reals.

pi, e
Intervals enclosing the constants $\pi$ and e respectively.

inf
Floating point infinity.

+Expr
Identity.

-Expr
Sign change.

+-Expr
Expr or -Expr. The result is an interval enclosing both, unless the expression is infeasible, in which case, an error is thrown.

abs(Expr)
The absolute value of Expr.

E1+E2
Addition.

E1-E2
Subtraction.

E1*E2
Multiplication.

E1/E2
Division.

E1\textasciicircum{}E2
Exponentiation.

min(E1,E2)
Minimum.

max(E1,E2)
Maximum.

sqr(Expr)
Square. Logically equivalent to Expr*Expr, but with better operational behaviour.

sqrt(Expr)
Square root (always positive).

exp(Expr)
Same as e^Expr.

ln(Expr)
Natural logarithm, the reverse of the exp function.

sin(Expr)
Sine.

cos(Expr)
Cosine.

atan(Expr)
Arcus tangens.

rsqr(Expr)
Reverse of the sqr function. Equivalent to +-sqrt(Expr).

rpow(E1,E2)
Reverse of exponentiation. i.e. finds X in E1 = X^E2.

sub(Expr)
A subinterval of Expr.

sum(ExprList)
Sum of a list of expressions.

min(ExprList)
Minimum of a list of expressions.

max(ExprList)
Maximum of a list of expressions.

and
Reified constraint conjunction. eg ic:(B =:= ((X>3) and (X<8)))

or
Reified constraint disjunction. eg ic:(B =:= ((X>3) or (X<8)))

=>
Reified constraint implication. eg ic:(B =:= ((X>3) => (X<8)))

neg
Reified constraint negation. eg ic:(B =:= (neg (X>3)))

>, >=, =:=, =<, <, =\=, #>, #>=, #=, #=<, #<, #\=, and, or, =>, neg
Any arithmetic or logical constraint that can be issued as a goal may also appear within an expression.

Within the expression context, the constraint evaluates to its reified truth value. If the constraint is entailed by the state of the constraint store then the (sub-)expression evaluates to 1. If it is dis-entailed by the state of the constraint store then it evaluates to 0. If its reified status is unknown then it evaluates to an integral variable 0..1.

Note: The simple cases (eg. ic:(Bool =:= (X > 5))) are equivalent to directly calling the reified forms of the basic constraints (eg. ic:(>(X,5,Bool))).

foo(Arg1, Arg2 ... ArgN), module:foo(Arg1, Arg2 ... ArgN)
Any terms found in the expression whose principle functor is not listed above will be replaced in the expression by a newly created auxiliary variable. This same variable will be appended to the term as an extra argument, and then the term will be called as |call(foo(Arg1, Arg2 ... ArgN, Aux))|. If no lookup module is specified, then the current module will be used.

This behaviour mimics that of is/2.


next up previous index
Next: Library Predicates Up: IC: A Hybrid Finite Previous: IC: A Hybrid Finite   Index
Warwick Harvey
2002-05-15