CSE 250A LECTURE NOTES

January 24, 2001
 
 

ANNOUNCEMENTS

Today's handout is the paper Minimizing Conflicts: A Heuristic Repair Method for Constraint-Satisfaction and Scheduling Problems by Steven Minton and others.  This paper shows how some research on neural networks is closely related to work on search algorithms.  CSPs are still an active, very interesting area.

For more information on a major real-world application of CSP methods, see SPIKE: Intelligent Scheduling of Hubble Space Telescope Observations by Mark D. Johnston and Glenn E. Miller.
 
 

ARC CONSISTENCY

Reminder: The arc (Vi, Vj) is arc-consistent if for every value x in the current domain Di of Vi, there exists some value y in the current domain Dj of Vj such that (x, y) is permitted by the binary constraint on Vi and Vj.

An arc (Vi, Vj) can always be made consistent by deleting those values from Di for which the condition is false.  Let this procedure be called Revise(Vi given Vj).  Formally the procedure is as follows:

procedure Revise(Vi given Vj):
    for each x in Di do {
        for each y in Dj do if (x, y) is consistent then next x;
        remove x from Di
        }
The forward-checking algorithm performs Revise(Vi given Vj) each time Vj is assigned a value.  We only need to do this for i > j because all variables i < j have been instantiated with a single value when Vj is instantiated.

Arc consistency is not symmetric in general.  Consider two neighboring countries in a map, say V1 and V2, whose current domains are the sets {red} and {red, blue}.  Then (V1, V2) is arc-consistent but (V2, V1) is not arc-consistent.
 
 

THE AC-3 ALGORITHM

We can do more than just forward-checking.  In general, whenever a value is eliminated for a variable Vk, we could (or should?) perform Revise(Vi given Vk) for all i different from k.  This means that whenever one Revise operation removes a value from a domain, we should perform further Revise operations.

One algorithm that performs Revise repeatedly until no more values can be eliminated from any domain is called AC-3, published by Alan Mackworth in 1977.  AC-3 uses Revise as a subroutine, with a flag to indicate whether each call to Revise(Vi given Vj) actually led to any change in Vi.

procedure Revise(Vi given Vj):
    changedVi := false
    for each x in Di do {
        for each y in Dj do if (x, y) is consistent then next x;
        remove x from Di
        changedVi := true
        }
    return changedVi
procedure AC-3:
    A := { (Vi, Vj) s.t. a constraint links Vi, Vj }
    Q := A
    while Q not empty {
        select and remove one arc (Vk, Vm) from Q
        if Revise(Vk given Vm) == true then
            Q := Q union { (Vi, Vk) in A with i =/= m }
        }
The AC-3 procedure can be executed at any time during backtracking search.  After AC-3 finishes, there are three alternative outcomes: Continuing search means branching on the possible values of a variable with two or more remaining values.

Note that while forward checking can easily be applied to three-way and higher arity constraints, AC-3 constraint propagation is specific to binary constraints.
 
 

A HEURISTIC FOR VARIABLE ORDERING

For soundness and completeness, it does not matter in which order variables are chosen for instantiation.  In order to improve efficiency (we hope) we can do dynamic variable ordering, i.e. use different orderings in different parts of the search tree (also called search rearrangement).

Note that we have two nested choice points:

If we reach inconsistency after choosing one particular variable, we do not need to try other variables at the same choice point.  But if we reach inconsistency after choosing one particular value, we do need to try all other possible values for this variable.

A good heuristic for reducing computational complexity is always to instantiate next the remaining variable that is most constrained, i.e. the one whose domain contains the fewest remaining values.

Why is this a good idea?  We only need to try one variable, so we would ideally choose the variable that leads to inconsistency being detected the quickest, in order to prune away this whole part of the search tree as quickly as possible.  The most constrained variable is a guess at the ideal variable.

By definition, heuristics are not guaranteed to be effective.  Even when they appear to work in practice, we cannot prove that they will always work, or prove why they work.  It is difficult to do good research on heuristics, because it is difficult to establish conclusions definitively.  For example, the most constrained variable ordering heuristic might be beneficial, but possibly not for the reason suggested above.
 
 

A HEURISTIC FOR VALUE ORDERING

Given that we have decided which variable to instantiate, in what order should we try its values that have not been eliminated already by constraint propagation?

As mentioned above, if a value leads to failure (i.e. inconsistency), we must still try the other values.  Only if a value leads to success (i.e. to a goal node) can we stop searching.  So, we should try first a value that seems most likely to lead to success.  This is a value that causes the smallest reduction in available values for other variables.

In other words, we should choose the value that allows the most flexibility for the still uninstantiated variables.  One way to make this idea precise is as follows:

for each value x of Vi separately, perform forward checking
measure the size of the remaining domains of all uninstantiated variables
sort the values x of Vi according to this measure.
If one value of Vi does lead to a goal node, then the forward checking done on other values of Vi will be wasted work.  Otherwise, if we do have to consider all values of Vi, then performing forward checking in advance will not impose any net cost in time (although it may in space).
 
 



Copyright (c) by Charles Elkan, 2001.