implementing constraints. 2 overview a look at the literature csps arc consistency algorithms ...

Post on 13-Jan-2016

219 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Implementing Constraints

2

Overview

A look at the literatureCSPs

Arc consistency algorithms

Implementation in ECLiPSe:Low level: Suspensions and Attributes

Prototyping in ECLiPSe:Intermediate: Constraint Handling Rules (CHR)

High level: Propia

A look at the literatureCSPs

Arc consistency algorithms

Implementation in ECLiPSe:Low level: Suspensions and Attributes

Prototyping in ECLiPSe:Intermediate: Constraint Handling Rules (CHR)

High level: Propia

3

Constraint Satisfaction Problems (CSP)

Much of the literature talks about “CSPs”

This usually refers to binary CSPs:

A fixed set of variables X1,…Xn

Every variable has a finite domain Di arbitrary domain, does not have to be ordered

Binary (2-variable) constraints only cij(Xi,Xj) constraint defined as sets of consistent value pairs

Any CSP can be transformed to binary CSPWe don’t normally do that

Instead generalise binary techniques

4

Static properties of a CSP network

Node consistencyvDi: ci(v)

Not very interesting

Arc consistencyvDi w Dj : cij(v,w)

Most relevant

Path consistencyvDi wDj u Dk: cik(v,u),ckj(u,w)

Usually too expensive

5

Arc consistency algorithms

Consistency in networks of relations [AC1-3]A.K. Mackworth, in Artificial Intelligence 8, pages 99-118, 1977.

Arc and path consistency revised [AC4]R. Mohr and T.C. Henderson, in Artificial Intelligence 28, pages 225-233, 1986.

A generic arc-consistency algorithm and its specializations [AC5]P. Van Hentenryck, Y. Deville, and C.-M. Teng, in Artificial Intelligence 57, pages 291-

321, 1992.

Arc-consistency and arc-consistency again [AC6]C. Bessiere, in Artificial Intelligence 65, pages 179-190, 1994.

Using constraint metaknowledge to reduce arc consistency

computation [AC7]C. Bessiere, E.C. Freuder, and J.-R. Régin, in Artificial Intelligence 107, pages 125-

148, 1999.

6

AC-1

procedure REVISE(Vi,Vj) DELETE <- false; for each X in Di do if there is no such Y in Dj such that (X,Y) is consistent, then delete X from Di; DELETE <- true; endif; endfor; return DELETE;

procedure AC-1 end REVISE Q <- {(Vi,Vj) in arcs(G),i#j}; repeat CHANGE <- false; for each (Vi,Vj) in Q do CHANGE <- REVISE(Vi,Vj) or CHANGE; endfor until not(CHANGE) end AC-1

G is the constraint graph, Q is a queue

7

AC-3

procedure AC-3 Q <- {(Vi,Vj) in arcs(G),i#j}; while not Q empty select and delete any arc (Vk,Vm) from Q; if REVISE(Vk,Vm) then Q <- Q union {(Vi,Vk) such that

(Vi,Vk) in arcs(G),i#k,i#m}

endif endwhile end AC-3

Re-check only arcs that were affected Queue contains arcs (constraints) This is a practical algorithm Easy to generalise to n-ary constraints

8

AC-4 - initialization

procedure INITIALIZE Q <- {}; S <- {}; % initialize each element of structure S for each (Vi,Vj) in arcs(G) do % (Vi,Vj) and (Vj,Vi) are same

elements for each a in Di do total <- 0; for each b in Dj do if (a,b) is consistent according to the constraint (Vi,Vj) then total <- total+1; Sj,b <- Sj,b union {<i,a>}; endif endfor; counter[(i,j),a] <- total; if counter[(i,j),a]=0 then delete a from Di; Q <- Q union {<i,a>}; endif; endfor; endfor; return Q; end INITIALIZE

9

AC-4 - fixpoint computation

procedure AC-4 Q <- INITIALIZE; while not Q empty select and delete any pair <j,b> from Q; counter[(i,j),a] <- counter[(i,j),a] - 1; for each <i,a> from Sj,b do if counter[(i,j),a]=0 & a is still in Di then delete a from Di; Q <- Q union {<i,a>}; endif endfor endwhile end AC-4

Maintains support counters for each domain element

Queue contains information about the deleted domain value

10

Relation to Search

These algorithms look at the constraint network statically

They are not strong enough to enforce global consistency

of the network, so we still need search

Search decisions change some domain(s), which means

we may lose any achieved xxx-consistency

Overall design needs to answer 2 questions: What level(s) of consistency do we want to employ? At what time (during search) do we want which consistency?

11

Consistency during search

Generate and testInstantiate all problem variables, then check all constraints

Standard backtrackingCheck every constraint as soon as both its variables are instantiated

Forward Checking (~ Label Propagation) Look ahead

When one variable is instantiated make the constraint arc consistent

When a variable is instantiated, make the whole graph arc consistent again (maintain arc consistency at all times)

(This terminology used in P.v.Hentenryck: Constraint Satisfaction in LP)

12

Other forms of consistency

Interval (bounds) consistencyUseful for ordered domainsWe don’t look at every domain valueOnly make sure that smallest and largest domain value are consistentDepending on the constraint semantics, bounds consistency implies arc

consistency, or not

Used in many ECLiPSe librarieslib(fd) - integer domainlib(ic) - integer and real number domainlib(ic_sets) - integer set domainlib(ic_symbolic) - ordered symbolic domain…

Implementing Constraints with Suspensions and Atrributes

14

What you need to know

To implement additional constraint for existing solver: suspend / resume mechanism the solver’s domain access interface

To implement a solver over a new domain: variable attribute mechanism

15

Basic Programming Support

Suspending the execution of goalsdelay-clause or suspend/3,4

Corresponds to the queue in AC-3!

Data/Event-driven waking on changeattaching to variables + condition

Change notifications Allows to say when computation should happen

messages from variables to constraints (see lib(notify_ports)) Information about what changed (for AC-4 style algorithms)

Priorities for goals Allows to tune efficiency

Suspending the execution of goalsdelay-clause or suspend/3,4

Corresponds to the queue in AC-3!

Data/Event-driven waking on changeattaching to variables + condition

Change notifications Allows to say when computation should happen

messages from variables to constraints (see lib(notify_ports)) Information about what changed (for AC-4 style algorithms)

Priorities for goals Allows to tune efficiency

16

Resolvent in ECLiPSe

q1, …, qm,

schedule(wake)

p1 , … , pn . Delayed Goals

suspend (delay)

s1

s4sl

s2 s3

r1, …, rk,

Prio 1 Prio 2 … Prio 12

17

Consistency check

capacity(1, N) :- N>=0.0, N=<350.0.

capacity(2, N) :- N>=0.0, N=<180.0.

capacity(3, N) :- N>=0.0, N=<50.0.

capacity(1, N) :- N>=0.0, N=<350.0.

capacity(2, N) :- N>=0.0, N=<180.0.

capacity(3, N) :- N>=0.0, N=<50.0.

delay capacity(T,N) if var(T);var(N).delay capacity(T,N) if var(T);var(N).

capacity(T,N) :- (var(T);var(N)), !,

suspend(capacity(T,N), 0, [T,N]->inst).

capacity(1, N) :- N>=0.0, N=<350.0.

capacity(2, N) :- N>=0.0, N=<180.0.

capacity(3, N) :- N>=0.0, N=<50.0.

capacity(T,N) :- (var(T);var(N)), !,

suspend(capacity(T,N), 0, [T,N]->inst).

capacity(1, N) :- N>=0.0, N=<350.0.

capacity(2, N) :- N>=0.0, N=<180.0.

capacity(3, N) :- N>=0.0, N=<50.0.

Declarative style

Imperative style

18

Forward Checking

:- lib(ic).

delay capacity(Type,N) if var(Type),var(N).

capacity(Type, N) :- var(N),

( Type=1 -> N :: 0.0..350.0

; Type=2 -> N :: 0.0..180.0

; Type=3 -> N :: 0.0..50.0

; fail

).

capacity(Type, N) :- nonvar(N),

N>=0.0,

( N=<50.0 -> Type :: [1,2,3]

; N=<180.0 -> Type :: [1,2]

; N=<350.0 -> Type = 1

; fail

).

:- lib(ic).

delay capacity(Type,N) if var(Type),var(N).

capacity(Type, N) :- var(N),

( Type=1 -> N :: 0.0..350.0

; Type=2 -> N :: 0.0..180.0

; Type=3 -> N :: 0.0..50.0

; fail

).

capacity(Type, N) :- nonvar(N),

N>=0.0,

( N=<50.0 -> Type :: [1,2,3]

; N=<180.0 -> Type :: [1,2]

; N=<350.0 -> Type = 1

; fail

).

19

Constraint via Propagation Goals

X Yc

X Y

c_fwd

c_bwd

X Yc_prop

Alternativelyimplemented

as

20

Forward Checking - 2 agents

capacity(Type, N) :-

capacity_forward(Type, N),

capacity_backward(Type, N).

delay capacity_forward(Type, _N) if var(Type).

capacity_forward(Type, N) :-

( Type=1 -> N :: 0.0..350.0

; Type=2 -> N :: 0.0..180.0

; Type=3 -> N :: 0.0..50.0

; fail ).

delay capacity_backward(_Type, N) if var(N).

capacity_backward(Type, N) :- N>=0.0,

( N=<50.0 -> Type :: [1,2,3]

; N=<180.0 -> Type :: [1,2]

; N=<350.0 -> Type = 1

; fail ).

capacity(Type, N) :-

capacity_forward(Type, N),

capacity_backward(Type, N).

delay capacity_forward(Type, _N) if var(Type).

capacity_forward(Type, N) :-

( Type=1 -> N :: 0.0..350.0

; Type=2 -> N :: 0.0..180.0

; Type=3 -> N :: 0.0..50.0

; fail ).

delay capacity_backward(_Type, N) if var(N).

capacity_backward(Type, N) :- N>=0.0,

( N=<50.0 -> Type :: [1,2,3]

; N=<180.0 -> Type :: [1,2]

; N=<350.0 -> Type = 1

; fail ).

21

Explicitly Suspending Goals

Providing more flexibility than delay-clauses Creation

make_suspension(+Goal, +Priority, -Suspension)

Attaching to attributed variableinsert_suspension(+Vars, +Suspension, +Index,

+AttributeName)

Combined create & attachsuspend(+Goal, +Priority, +Condition)suspend(+Goal, +Priority, +Condition, -Suspension)

Delayed goal viewer shows suspended goals

Providing more flexibility than delay-clauses Creation

make_suspension(+Goal, +Priority, -Suspension)

Attaching to attributed variableinsert_suspension(+Vars, +Suspension, +Index,

+AttributeName)

Combined create & attachsuspend(+Goal, +Priority, +Condition)suspend(+Goal, +Priority, +Condition, -Suspension)

Delayed goal viewer shows suspended goals

22

Triggering of suspensions

When goals (constraints) are suspended, they are usually

attached to variables with trigger conditions:

X->instwhen X becomes instantiated (most specific)

X->constrainedwhen X becomes constrained in any way (most general)

X->ic:min, X->ic:max, X->ic:hole, X->typewhen the lower bound / upper bound / other value in the domain of X changes

Other trigger conditions are defined by the various

solvers

When goals (constraints) are suspended, they are usually

attached to variables with trigger conditions:

X->instwhen X becomes instantiated (most specific)

X->constrainedwhen X becomes constrained in any way (most general)

X->ic:min, X->ic:max, X->ic:hole, X->typewhen the lower bound / upper bound / other value in the domain of X changes

Other trigger conditions are defined by the various

solvers

23

Bounds consistency

ge(X, Y) :-

get_max(X, XH), % get current

bounds

get_min(Y, YL),

impose_min(X, YL), % impose new bounds

impose_max(Y, XH).

ge(X, Y) :-

get_max(X, XH), % get current

bounds

get_min(Y, YL),

impose_min(X, YL), % impose new bounds

impose_max(Y, XH).

X >= Y

( var(X),var(Y) ->

suspend(ge(X,Y), 0, [[X,Y]->constrained])

; true ),

( var(X),var(Y) ->

suspend(ge(X,Y), 0, [[X,Y]->constrained])

; true ),

Bounds-consistent greater-equal:Bounds-consistent greater-equal:

24

More precise waking conditions

ge(X, Y) :-

( var(X),var(Y) ->

suspend(ge(X,Y), 0, [X->ic:max,Y->ic:min])

; true ),

get_max(X, XH),

get_min(Y, YL),

impose_min(X, YL),

impose_max(Y, XH).

ge(X, Y) :-

( var(X),var(Y) ->

suspend(ge(X,Y), 0, [X->ic:max,Y->ic:min])

; true ),

get_max(X, XH),

get_min(Y, YL),

impose_min(X, YL),

impose_max(Y, XH).

X >= YBounds-consistent greater-equal:Bounds-consistent greater-equal:

25

Variables, Attributes and Suspended Goals

X { suspend: inst constrained ic: 1..9 min max hole}

X { suspend: inst constrained ic: 1..9 min max hole}

Y { suspend: inst constrained ic: 1..9 min max hole}

Y { suspend: inst constrained ic: 1..9 min max hole}

ge(X,Y) ge(X,Y)

26

Directional propagators

ge(X, Y) :-

ge_fwd(X,Y),

ge_bwd(X,Y).

ge_fwd(X, Y) :-

( var(X) -> suspend(ge_fwd(X,Y), 0, [X->ic:max])

; true ),

get_max(X, XH),

impose_max(Y, XH).

ge_bwd(X, Y) :-

( var(Y) -> suspend(ge_bwd(X,Y), 0, [Y->ic:min])

; true ),

get_min(Y, YL),

impose_min(X, YL).

ge(X, Y) :-

ge_fwd(X,Y),

ge_bwd(X,Y).

ge_fwd(X, Y) :-

( var(X) -> suspend(ge_fwd(X,Y), 0, [X->ic:max])

; true ),

get_max(X, XH),

impose_max(Y, XH).

ge_bwd(X, Y) :-

( var(Y) -> suspend(ge_bwd(X,Y), 0, [Y->ic:min])

; true ),

get_min(Y, YL),

impose_min(X, YL).

X >= Y

27

ge_bwd(X,Y) ge_bwd(X,Y)

ge_fwd(X,Y) ge_fwd(X,Y)

Directional Propagators

X { suspend: inst constrained ic: 1..9 min max hole}

X { suspend: inst constrained ic: 1..9 min max hole}

Y { suspend: inst constrained ic: 1..9 min max hole}

Y { suspend: inst constrained ic: 1..9 min max hole}

28

Repeated waking on domain updates

E.g. propagating boundsge(X, Y) :-

( var(X),var(Y) ->

suspend(ge(X,Y), 0, [X->ic:max, Y->ic:min])

; true ),

get_max(X, XH),

get_min(Y, YL),

impose_min(X, YL), % impose new bounds

impose_max(Y, XH).

Propertiesvariables remain variablesarbitrary number of steps (limited only by domain size)

Potential performance problem:re-suspending identical goal over and over again!

E.g. propagating boundsge(X, Y) :-

( var(X),var(Y) ->

suspend(ge(X,Y), 0, [X->ic:max, Y->ic:min])

; true ),

get_max(X, XH),

get_min(Y, YL),

impose_min(X, YL), % impose new bounds

impose_max(Y, XH).

Propertiesvariables remain variablesarbitrary number of steps (limited only by domain size)

Potential performance problem:re-suspending identical goal over and over again!

X >= Y

29

Domain/bounds propagation using a “demon”

Same constraint as before:ge(X, Y) :-

suspend(ge(X,Y,Susp), 0, [X->ic:max, Y->ic:min], Susp),

ge(X, Y, Susp).

:- demon ge/3. % demon declaration

ge(X, Y, Susp) :-

( var(X),var(Y) -> true % implicitly re-suspend

; kill_suspension(Susp) ), % explicit kill

get_max(X, XH),

get_min(Y, YL),

impose_min(X, YL), % impose new bounds

impose_max(Y, XH).

A “demon” does not need to be re-suspendedWhen woken, it splits into a woken and a suspended instance

Needs to be killed explicitly when no longer needed

Same constraint as before:ge(X, Y) :-

suspend(ge(X,Y,Susp), 0, [X->ic:max, Y->ic:min], Susp),

ge(X, Y, Susp).

:- demon ge/3. % demon declaration

ge(X, Y, Susp) :-

( var(X),var(Y) -> true % implicitly re-suspend

; kill_suspension(Susp) ), % explicit kill

get_max(X, XH),

get_min(Y, YL),

impose_min(X, YL), % impose new bounds

impose_max(Y, XH).

A “demon” does not need to be re-suspendedWhen woken, it splits into a woken and a suspended instance

Needs to be killed explicitly when no longer needed

30

Variables, Attributes and Suspended Demon

X { suspend: inst constrained ic: 1..9 min max hole}

X { suspend: inst constrained ic: 1..9 min max hole}

Y { suspend: inst constrained ic: 1..9 min max hole}

Y { suspend: inst constrained ic: 1..9 min max hole}

ge(X,Y,Susp) ge(X,Y,Susp)

31

Single-propagator max-constraint

mymax(A, B, M):- get_bounds(A, MinA, MaxA),

get_bounds(B, MinB, MaxB),get_bounds(M, MinM, MaxM),

( MinA >= MaxB -> A = M

; MinB >= MaxA -> B = M; MinM > MaxB -> A = M; MinM > MaxA -> B = M;

call_priority((Max is max(MaxA, MaxB),Min is max(MinA, MinB),impose_bounds(M, Min, Max), impose_max(A, MaxM),impose_max(B, MaxM),

Vars = [A,B,M],( nonground(2, Vars, _) ->

suspend(mymax(A, B, M), 3, [Vars->ic:max,Vars->ic:min]); true)

), 2)).

32

N-ary constraints

Even more implementation choices: Level of consistency Algorithm Eagerness

Trigger conditions and priority

Logical decompositione.g. alldifferent many disequality constraints

Operational decompositionOne or several propagator goals

Data-driven vs explicit fixpoint computation

33

Special case: Incremental checking (1)

Value X occurs in List :among(X, [Y|Ys]) :-

( var(Y) ->

suspend(among(X, [Y|Ys]),0,Y->inst)

; X = Y ->

true

;

among(X, Ys)

).

Propertiesefficient, looks at most once at each list element

delayed goal may be different after each step

success after 1..N steps or failure after N steps

Value X occurs in List :among(X, [Y|Ys]) :-

( var(Y) ->

suspend(among(X, [Y|Ys]),0,Y->inst)

; X = Y ->

true

;

among(X, Ys)

).

Propertiesefficient, looks at most once at each list element

delayed goal may be different after each step

success after 1..N steps or failure after N steps

34

Incremental checking (2)

Value X does not occur in List:not_among(X, []).not_among(X, [Y|Ys]) :-

( var(Y) -> suspend(not_among(X, [Y|Ys]),0,Y->inst); X \= Y, not_among(X, Ys)).

PropertiesSuccess after N steps or failure after 1..N stepsBut: there may be an X at the end of the list, behind a variable! Failure can be detected unnecessarily late

Value X does not occur in List:not_among(X, []).not_among(X, [Y|Ys]) :-

( var(Y) -> suspend(not_among(X, [Y|Ys]),0,Y->inst); X \= Y, not_among(X, Ys)).

PropertiesSuccess after N steps or failure after 1..N stepsBut: there may be an X at the end of the list, behind a variable! Failure can be detected unnecessarily late

35

Parallel checking

Value X does not occur in List:not_among(X, []).not_among(X, [Y|Ys]) :-

( var(Y) -> suspend(X\=Y, 0, Y->inst); X \= Y),

not_among(X, Ys).

PropertiesCan expand into up to N delayed goals X\=YFailure detected as soon as possible Normally more interesting than detecting success!

Value X does not occur in List:not_among(X, []).not_among(X, [Y|Ys]) :-

( var(Y) -> suspend(X\=Y, 0, Y->inst); X \= Y),

not_among(X, Ys).

PropertiesCan expand into up to N delayed goals X\=YFailure detected as soon as possible Normally more interesting than detecting success!

37

Implementing a new domain

:- module(enum). % library name

:- meta_attribute(enum, [ % attribute name and handlers

unify: unify_enum/2,

print: print_enum/2,

compare_instances: ...,

copy_term: ...]).

make_enum_variable(Var, Values) :- % constructor

Attr = enum(Values,_),

init_suspension_list(2, Attr),

add_attribute(Var, Attr).

unify_enum(Value, Attribute) :- % unify handler

check if Value is compatible with Attribute

exclude(Var{Attribute}, Value) ?- % domain access primitive

delete Value from possible values in Attribute

...

38

Exercise 1

Write a constraint

atmost(+N, +List, +Value) Meaning: at most N elements of List have value

Value Behaviour: fail as soon as more than N elements are

instantiated to Value Improvement: fail as soon as not enough variables

with Value in their domain are left over

Write a constraint

atmost(+N, +List, +Value) Meaning: at most N elements of List have value

Value Behaviour: fail as soon as more than N elements are

instantiated to Value Improvement: fail as soon as not enough variables

with Value in their domain are left over

39

Exercise 2

Write a constraintoffset(?X,+C,?Y)

which is like

offset(X,C,Y) :- Y #= X+C.But maintains domain consistency (propagates “holes”)

Use Suspension built-ins Domain access primitives from the ic_kernel module

40

Domain attribute access in lib(ic)

Getting domain representation from a variableget_domain_as_list(+Var, -ListOfValues)

get_bounds(+Var, -Min, -Max)

get_min(+Var, -Min)

get_max(+Var, -Max)

get_domain_size(+Var, -Size)

...

Updating a variable’s domainimpose_bounds(+Var, +Min, +Max)

impose_min(+Var, +Min)

impose_max(+Var, +Max)

exclude(+Var, +Value)

...

Getting domain representation from a variableget_domain_as_list(+Var, -ListOfValues)

get_bounds(+Var, -Min, -Max)

get_min(+Var, -Min)

get_max(+Var, -Max)

get_domain_size(+Var, -Size)

...

Updating a variable’s domainimpose_bounds(+Var, +Min, +Max)

impose_min(+Var, +Min)

impose_max(+Var, +Max)

exclude(+Var, +Value)

...

top related